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