// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: /CounterStrike/EVENT.CPP 1 3/03/97 10:24a Joe_bostic $ */ /*********************************************************************************************** *** C O N F I D E N T 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 : November 10, 1995 [BRR] * * * *---------------------------------------------------------------------------------------------* * 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. * * EventClass::EventClass -- construct a variable-sized event * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" #ifdef WIN32 #include "ccdde.h" #endif //WIN32 #ifdef FIXIT_VERSION_3 // Stalemate games. #include "WolStrng.h" #endif /*************************************************************************** ** 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.MegaMission_F ), // MEGAMISSION_F 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.SellCell), // SELLCELL size_of(EventClass, Data.Options ), // SPECIAL 0, // FRAMESYNC 0, // MESSAGE size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME size_of(EventClass, Data.FrameInfo ), // FRAMEINFO 0, // SAVEGAME size_of(EventClass, Data.NavCom ), // ARCHIVE size_of(EventClass, Data.Variable.Size), // ADDPLAYER size_of(EventClass, Data.Timing ), // TIMING size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME #ifdef FIXIT_VERSION_3 // Stalemate games. 0, // PROPOSE_DRAW 0, // RETRACT_DRAW #endif }; char * EventClass::EventNames[EventClass::LAST_EVENT] = { "EMPTY", "ALLY", "MEGAMISSION", "MEGAMISSION_F", "IDLE", "SCATTER", "DESTRUCT", "DEPLOY", "PLACE", "OPTIONS", "GAMESPEED", "PRODUCE", "SUSPEND", "ABANDON", "PRIMARY", "SPECIAL_PLACE", "EXIT", "ANIMATION", "REPAIR", "SELL", "SELLCELL", "SPECIAL", "FRAMESYNC", "MESSAGE", "RESPONSE_TIME", "FRAMEINFO", "SAVEGAME", "ARCHIVE", "ADDPLAYER", "TIMING", "PROCESS_TIME", #ifdef FIXIT_VERSION_3 // Stalemate games. "PROPOSE_DRAW", "RETRACT_DRAW", #endif }; /*********************************************************************************************** * 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 = PlayerPtr->ID; 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, TargetClass target) { ID = PlayerPtr->ID; Type = type; Frame = ::Frame; Data.Target.Whom = target; } EventClass::EventClass(EventType type, CELL cell) { ID = PlayerPtr->ID; Type = type; Frame = ::Frame; Data.SellCell.Cell = cell; } /*********************************************************************************************** * 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 = PlayerPtr->ID; 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 = PlayerPtr->ID; 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, TargetClass src, TargetClass dest) { ID = PlayerPtr->ID; Type = type; Frame = ::Frame; Data.NavCom.Whom = src; Data.NavCom.Where = TargetClass(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, int visible) { ID = PlayerPtr->ID; Type = ANIMATION; Frame = ::Frame; Data.Anim.What = anim; Data.Anim.Owner = owner; Data.Anim.Where = coord; Data.Anim.Visible = visible; } /*********************************************************************************************** * 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(TargetClass src, MissionType mission, TargetClass target, TargetClass destination) { ID = PlayerPtr->ID; Type = MEGAMISSION; Frame = ::Frame; Data.MegaMission.Whom = src; Data.MegaMission.Mission = mission; Data.MegaMission.Target = target; Data.MegaMission.Destination = destination; } /*********************************************************************************************** * 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). This variation is used for formation moves. * * * * 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. * * * * speed -- The formation override speed for this move. * * * * maxspeed -- The formation override maximum speed for this move. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 05/18/1995 JLB : Created. * *=============================================================================================*/ EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed) { ID = PlayerPtr->ID; Type = MEGAMISSION_F; Frame = ::Frame; Data.MegaMission_F.Whom = src; Data.MegaMission_F.Mission = mission; Data.MegaMission_F.Target = TargetClass(target); Data.MegaMission_F.Destination = TargetClass(destination); Data.MegaMission_F.Speed = speed; Data.MegaMission_F.MaxSpeed = maxspeed; } /*********************************************************************************************** * 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 = PlayerPtr->ID; 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 = PlayerPtr->ID; 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 = PlayerPtr->ID; Type = type; Frame = ::Frame; Data.Special.ID = id; Data.Special.Cell = cell; } /*********************************************************************************************** * EventClass::EventClass -- construct a variable-sized event * * * * INPUT: * * ptr ptr to data associated with this event * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 11/10/1995 BRR : Created. * *=============================================================================================*/ EventClass::EventClass(EventType type, void * ptr, unsigned long size) { ID = PlayerPtr->ID; Type = type; Frame = ::Frame; Data.Variable.Pointer = ptr; Data.Variable.Size = size; } /*********************************************************************************************** * 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; // CELL cell = 0; char txt[80]; bool formation = false; // RTTIType rt; if (Debug_Print_Events) { printf("(%d) Executing %s ID:%d Frame:%d ", ::Frame, EventNames[Type], ID, Frame); } switch (Type) { /* ** Update the archive target for this building. */ case ARCHIVE: techno = Data.NavCom.Whom.As_Techno(); if (techno && techno->IsActive) { techno->ArchiveTarget = Data.NavCom.Where.As_TARGET(); } break; /* ** 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: Houses.Raw_Ptr(ID)->Flag_To_Die(); 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); // Should be IniName? ST - 5/8/2019 sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->IniName); Session.Messages.Add_Message(NULL, 0, txt, house->RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200); 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: techno = Data.Target.Whom.As_Techno(); 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: techno = Data.Target.Whom.As_Techno(); if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { if (techno->What_Am_I() == RTTI_BUILDING || (techno->What_Am_I() == RTTI_UNIT && Map[techno->Center_Coord()].Cell_Building() != 0)) { techno->Sell_Back(-1); } } else { // if (Is_Target_Cell(Data.Target.Whom)) { // Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); // } } break; /* ** Tells the wall at the specified location to sell off. */ case SELLCELL: // cell = Data.SellCell.Cell; Houses.Raw_Ptr(ID)->Sell_Wall(Data.SellCell.Cell); 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) { anim->Set_Owner(Data.Anim.Owner); anim->Set_Visible_Flags(static_cast(Data.Anim.Visible)); /* ** Beacons have a 30-second kill time. */ if (Data.Anim.What == ANIM_BEACON) { FILETIME ft; GetSystemTimeAsFileTime(&ft); unsigned long long kill_time = ((unsigned long long)ft.dwLowDateTime + ((unsigned long long)ft.dwHighDateTime << 32ULL)) + 300000000ULL; anim->Kill_At(kill_time); } } 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: Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); break; /* ** This event starts production of the specified 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: 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: 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: Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); break; /* ** Toggles the primary factory state of the specified building. */ case PRIMARY: { BuildingClass * building = Data.Target.Whom.As_Building(); 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_F: techno = Data.MegaMission_F.Whom.As_Techno(); if (techno && techno->IsActive && techno->Is_Foot()) { ((FootClass *)techno)->IsFormationMove = true; ((FootClass *)techno)->FormationSpeed = Data.MegaMission_F.Speed; ((FootClass *)techno)->FormationMaxSpeed = Data.MegaMission_F.MaxSpeed; FormMove = true; FormSpeed = Data.MegaMission_F.Speed; FormMaxSpeed = Data.MegaMission_F.MaxSpeed; formation = true; } // Fall thru to next case... case MEGAMISSION: if (Debug_Print_Events) { printf("Whom:%x Tgt:%x Dest:%x ", Data.MegaMission.Whom.As_TARGET(), Data.MegaMission.Target.As_TARGET(), Data.MegaMission.Destination.As_TARGET()); } techno = Data.MegaMission.Whom.As_Techno(); if (techno != NULL && techno->IsActive && techno->Strength > 0 && !techno->IsInLimbo) { /* ** Fetch a pointer to the object of the mission. If there is an error with ** this object, such as it is dead, then bail. */ ObjectClass * object = NULL; if (Data.MegaMission.Target.Is_Valid()) { object = Data.MegaMission.Target.As_Object(); if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { break; // object = NULL; // Data.MegaMission.Target.Invalidate(); } } /* ** If the destination target is invalid because the object is dead, then ** bail from processing this mega mission. */ if (Data.MegaMission.Destination.Is_Valid()) { object = Data.MegaMission.Destination.As_Object(); if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { break; // object = NULL; // Data.MegaMission.Destination.Invalidate(); } } /* ** Break any existing tether or team contact, since it is now invalid. */ if (!techno->IsTethered) { techno->Transmit_Message(RADIO_OVER_OUT); } if (techno->Is_Foot()) { if (!formation) ((FootClass *)techno)->IsFormationMove = false; if (((FootClass *)techno)->Team) { ((FootClass *)techno)->Team->Remove((FootClass *)techno); } } if (object != NULL) { // 2019/09/20 JAS - Added record of who clicked on the object HouseClass* house = Houses.Raw_Ptr(ID); bool is_allied = house != nullptr && house->Is_Ally(techno); if (is_allied) { object->Clicked_As_Target((HousesType)ID); } } /* ** Test to see if the navigation target should really be queued rather ** than assigned to the object. This would be the case if this is a ** special queued move mission and there is already a valid navigation ** target for this unit. */ bool q = (Data.MegaMission.Mission == MISSION_QMOVE); techno->Assign_Mission(Data.MegaMission.Mission); if (techno->Is_Foot()) { ((FootClass*)techno)->SuspendedNavCom = TARGET_NONE; } techno->SuspendedTarCom = TARGET_NONE; /* ** 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 && techno->Is_Foot()) { techno->Assign_Target(TARGET_NONE); techno->Assign_Destination(Data.MegaMission.Target.As_TARGET()); techno->ArchiveTarget = Data.MegaMission.Target.As_TARGET(); } else if (Data.MegaMission.Mission == MISSION_ENTER && object != NULL && object->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)object) == STRUCT_REFINERY) { techno->Transmit_Message(RADIO_HELLO, (BuildingClass*)object); techno->Assign_Destination(TARGET_NONE); } else { if (q && techno->Is_Foot()) { ((FootClass *)techno)->Queue_Navigation_List(Data.MegaMission.Destination.As_TARGET()); } else { if (techno->Is_Foot()) { ((FootClass *)techno)->Clear_Navigation_List(); } techno->Assign_Target(Data.MegaMission.Target.As_TARGET()); techno->Assign_Destination(Data.MegaMission.Destination.As_TARGET()); } } // // Special case for ship repairing: If the assigned mission is to // move, and 'techno' is a Vessel: // If the destination is a shipyard or sub pen, set the IsToSelfRepair flag // Otherwise, clear both IsToSelfRepair and IsSelfRepairing // RTTIType rt = techno->What_Am_I(); // rt = Data.MegaMission.Whom; if (rt == RTTI_VESSEL && techno != NULL && techno->What_Am_I() == RTTI_VESSEL) { VesselClass *ship = (VesselClass *)techno; if (Data.MegaMission.Mission == MISSION_MOVE) { if (object != NULL) { if (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->House->Is_Ally(techno) && // if ((RTTIType)Data.MegaMission.Destination == RTTI_BUILDING && (((BuildingClass *)object)->Class->Type == STRUCT_SHIP_YARD || ((BuildingClass *)object)->Class->Type == STRUCT_SUB_PEN)) { ship->IsToSelfRepair = true; } else { ship->IsToSelfRepair = false; ship->IsSelfRepairing = false; } } else { ship->IsToSelfRepair = false; ship->IsSelfRepairing = false; } } else { ship->IsToSelfRepair = false; ship->IsSelfRepairing = false; } } #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 = Data.Target.Whom.As_Techno(); if (techno != NULL && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered && techno->What_Am_I() != RTTI_BUILDING) { techno->Transmit_Message(RADIO_OVER_OUT); techno->Assign_Destination(TARGET_NONE); techno->Assign_Target(TARGET_NONE); techno->Enter_Idle_Mode(); if (techno->Is_Foot()) { ((FootClass *)techno)->Clear_Navigation_List(); } } break; /* ** Request that the unit/infantry/aircraft scatter from its current location. */ case SCATTER: techno = Data.Target.Whom.As_Techno(); if (techno != NULL && techno->Is_Foot() && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { ((FootClass *)techno)->IsScattering = true; techno->Scatter(0, true, false); } break; /* ** If we are placing down the ion cannon blast then lets take ** care of it. */ case SPECIAL_PLACE: 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: Theme.Queue_Song(THEME_NONE); Stop_Speaking(); Speak(VOX_CONTROL_EXIT); while (Is_Speaking()) { Call_Back(); } GameActive = false; break; /* ** Process the options menu, unless we're playing back a recording. */ case OPTIONS: if (!Session.Play) { SpecialDialog = SDLG_OPTIONS; } break; /* ** Process the options Game Speed */ case GAMESPEED: Options.GameSpeed = Data.General.Value; break; /* ** Adjust connection timing for multiplayer games */ case RESPONSE_TIME: Session.MaxAhead = Data.FrameInfo.Delay; break; /* ** Save a multiplayer game (this event is only generated in multiplayer mode) */ case SAVEGAME: /* ** Show the user what's going on with a message box (but only if ** we're not already inside a dialog box routine!) */ if (SpecialDialog == SDLG_NONE) { CDTimerClass timer; //timer.Start(); timer = TICKS_PER_SECOND * 4; WWMessageBox().Process(TXT_SAVING_GAME, TXT_NONE); Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); while (timer > 0) { Call_Back(); } HidPage.Clear(); Map.Flag_To_Redraw(true); Map.Render(); } else { Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); } break; /* ** Add a new player to the game: ** - Form a network connection to him ** - Add his name, ID, House etc to our list of players ** - Re-sort the ID array ** - Place his units on the map */ case ADDPLAYER: int i; printf("ADDPLAYER EVENT!\n"); for (i=0;i<(int)Data.Variable.Size;i++) { printf("%d\n", ((char *)Data.Variable.Pointer)[i]); } if (ID != PlayerPtr->ID) { delete [] Data.Variable.Pointer; } 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: #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 > Session.MaxAhead) { NewMaxAheadFrame1 = Frame; NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; } #endif Session.DesiredFrameRate = Data.Timing.DesiredFrameRate; Session.MaxAhead = Data.Timing.MaxAhead; #ifndef WOLAPI_INTEGRATION /* ** If spawned from WChat then we should be getting poked every minute. If not then ** deliberately break the max ahead value */ #ifdef WIN32 if (Special.IsFromWChat) { Session.MaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); } #endif //WIN32 #endif // !WOLAPI_INTEGRATION if (Debug_Print_Events) { printf("DesiredFrameRate:%d MaxAhead:%d ", Session.DesiredFrameRate, Session.MaxAhead); } 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 < Session.Players.Count(); i++) { if (ID == Session.Players[i]->Player.ID) { Session.Players[i]->Player.ProcessTime = Data.ProcessTime.AverageTicks; break; } } break; #ifdef FIXIT_VERSION_3 // Stalemate games. case PROPOSE_DRAW: if( ID == PlayerPtr->ID ) { if( Scen.bOtherProposesDraw ) { // Both sides agree to draw. Game will end in a tie. Scen.bLocalProposesDraw = true; break; } Scen.bLocalProposesDraw = true; //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_PROPOSED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); } else { if( Scen.bLocalProposesDraw ) { // Both sides agree to draw. Game will end in a tie. Scen.bOtherProposesDraw = true; break; } char szMessage[ 100 ]; for (i = 0; i < Session.Players.Count(); i++) { if (ID == Session.Players[i]->Player.ID) { //PG sprintf( szMessage, TXT_WOL_DRAW_PROPOSED_OTHER, Session.Players[i]->Name ); break; } } Scen.bOtherProposesDraw = true; Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); } Sound_Effect( VOC_INCOMING_MESSAGE ); break; case RETRACT_DRAW: if( ID == PlayerPtr->ID ) { Scen.bLocalProposesDraw = false; //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_RETRACTED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); } else { char szMessage[ 100 ]; for (i = 0; i < Session.Players.Count(); i++) { if (ID == Session.Players[i]->Player.ID) { //PG sprintf( szMessage, TXT_WOL_DRAW_RETRACTED_OTHER, Session.Players[i]->Name ); break; } } Scen.bOtherProposesDraw = false; Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); } Sound_Effect( VOC_INCOMING_MESSAGE ); break; #endif /* ** Default: do nothing. */ default: break; } if (Debug_Print_Events) { printf("\n"); } }