diff --git a/GameInitOptionsScreen.cpp b/GameInitOptionsScreen.cpp index 7227d0b7..2ffe2d53 100644 --- a/GameInitOptionsScreen.cpp +++ b/GameInitOptionsScreen.cpp @@ -933,7 +933,7 @@ void BtnDifficultyTogglesCallback( GUI_BUTTON *btn, INT32 reason ) BOOLEAN fAnyChecked=FALSE; //if none of the other boxes are checked, do not uncheck this box - for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) diff --git a/GameSettings.cpp b/GameSettings.cpp index 4148cc1a..b6b86d87 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -987,6 +987,7 @@ void LoadGameAPBPConstants() void FreeGameExternalOptions() { + if (gGameExternalOptions.iaIMPSlots != NULL) // OJW - 20081129 - Fix memory leak when calling LoadGameExternalOptions twice MemFree( gGameExternalOptions.iaIMPSlots); } diff --git a/GameVersion.cpp b/GameVersion.cpp index 7212031c..9690bf1f 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -28,11 +28,11 @@ CHAR16 zVersionLabel[256] = { L"Beta v. 0.98" }; #else //RELEASE BUILD VERSION - CHAR16 zVersionLabel[256] = { L"Release v1.13.2620" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.2628" }; #endif -CHAR8 czVersionNumber[16] = { "Build 09.03.12" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 09.03.19" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; diff --git a/JA2.vcproj b/JA2.vcproj index 1163385d..ef60cbc2 100644 --- a/JA2.vcproj +++ b/JA2.vcproj @@ -1864,6 +1864,18 @@ BrowseInformation="1"/> + + + + + + + + + + + + + + + + diff --git a/Laptop/laptop.cpp b/Laptop/laptop.cpp index 41d37792..e460ff3a 100644 --- a/Laptop/laptop.cpp +++ b/Laptop/laptop.cpp @@ -1025,6 +1025,11 @@ void ExitLaptop() return; } + if (gfEnterLapTop) + { + // we have already exited the laptop, its not currently initialised + return; + } if ( DidGameJustStart() ) { SetMusicMode( MUSIC_LAPTOP ); diff --git a/MPChatScreen.cpp b/MPChatScreen.cpp new file mode 100644 index 00000000..520d733d --- /dev/null +++ b/MPChatScreen.cpp @@ -0,0 +1,1569 @@ +#ifdef PRECOMPILEDHEADERS + #include "JA2 All.h" +#else + #include "sgp.h" + #include "screenids.h" + #include "Timer Control.h" + #include "sys globals.h" + #include "fade screen.h" + #include "sysutil.h" + #include "vobject_blitters.h" + #include "MercTextBox.h" + #include "wcheck.h" + #include "cursors.h" + #include "font control.h" + #include "Game Clock.h" + #include "Map Screen Interface.h" + #include "renderworld.h" + #include "gameloop.h" + #include "english.h" + #include "GameSettings.h" + #include "Interface Control.h" + #include "cursor control.h" + #include "laptop.h" + #include "text.h" + #include "Text Input.h" + #include "overhead map.h" + #include "MPChatScreen.h" + #include "WordWrap.h" + #include "Render Dirty.h" +#include "message.h" +#include "utilities.h" +#include "connect.h" +#endif + +#define CHATBOX_WIDTH 310 // 350 is the max size, the PrepareMercPopupBox will add the X_MARGIN to both sides +#define CHATBOX_Y_MARGIN_NOLOG 25 +#define CHATBOX_Y_MARGIN_LOG 80 +#define CHATBOX_Y_MARGIN_BOTTOM 80 +#define CHATBOX_X_MARGIN 30 // pad box width 20 either side of text pixel length, but i use half of this value for control margins + +#define CHATBOX_LOG_WIDTH 300 +#define CHATBOX_LOG_HEIGHT 80 +#define CHATBOX_LOG_TXTBORDER 4 // offset till text renderable area on all sides + +#define CHATBOX_SCROLL_WIDTH 17 +#define CHATBOX_SCROLL_HEIGHT 80 +#define CHATBOX_SCROLL_GAPX 5 + +#define CHATBOX_BUTTON_WIDTH 61 +#define CHATBOX_BUTTON_HEIGHT 20 +#define CHATBOX_BUTTON_X_SEP 15 + +#define CHATBOX_SMALL_BUTTON_WIDTH 31 +#define CHATBOX_SMALL_BUTTON_X_SEP 8 + +#define CHATBOX_TOGGLE_WIDTH 120 +#define CHATBOX_TOGGLE_HEIGHT 12 + +#define CHATBOX_Y_GAP 10 +#define CHATBOX_SLIDER_GAP 5 + +#define CHATBOX_INPUT_HEIGHT 20 + +#define CHATBOX_FONT_COLOR 73 +#define CHATBOX_FONT_TITLE FONT14ARIAL +#define CHATBOX_FONT_TOGGLE FONT10ARIAL + +// old mouse x and y positions +extern SGPPoint pOldMousePosition; +SGPRect ChatBoxRestrictedCursorRegion; + +// if the cursor was locked to a region +extern BOOLEAN fCursorLockedToArea; +BOOLEAN gfInChatBox = FALSE; + +//extern BOOLEAN fMapExitDueToMessageBox; +extern BOOLEAN fInMapMode; +extern BOOLEAN gfOverheadMapDirty; + +//OJW - 20090208 +CHAR16 gszChatBoxInputString[255]; +BOOLEAN gbChatSendToAll = true; + +void OKChatBoxCallback(GUI_BUTTON *btn, INT32 reason ); +void CancelChatBoxCallback(GUI_BUTTON *btn, INT32 reason ); +void ChatBoxClickCallback( MOUSE_REGION * pRegion, INT32 iReason ); +UINT32 ExitChatBox( INT8 ubExitCode ); +UINT16 GetChatBoxButtonWidth( INT32 iButtonImage ); + +extern SGPRect gOldCursorLimitRectangle; + + +MESSAGE_BOX_STRUCT gChatBox; +BOOLEAN gfNewChatBox = FALSE; +extern BOOLEAN gfStartedFromGameScreen; +extern BOOLEAN gfStartedFromMapScreen; +BOOLEAN fRestoreBackgroundForChatBox = FALSE; +extern BOOLEAN gfDontOverRideSaveBuffer; //this variable can be unset if ur in a non gamescreen and DONT want the msg box to use the save buffer + + +extern void HandleTacticalUILoseCursorFromOtherScreen( ); +extern STR16 pUpdatePanelButtons[]; + + +#define NUM_CHAT_TOGGLES 2 +INT32 guiChatToggles[ NUM_CHAT_TOGGLES ]; +void BtnChatTogglesCallback(GUI_BUTTON *btn,INT32 reason); +void RestoreChatToggleBackGrounds(); + +bool gIncludeChatLog = false; +SGPRect gChatTextBoxRegion; + +// OPTIONAL CHAT MESSAGE LOG SETUP +SGPRect gChatMessageLogRegion; + +UINT32 guiCHATLOGIMG; // Images for chat message log + +// button enums +enum{ + CHAT_SCROLL_MESSAGE_UP =0, + CHAT_SCROLL_MESSAGE_DOWN, +}; + +UINT32 guiChatSliderBar; + +UINT16 CHATLOG_SCROLL_AREA_START_Y; //(SCREEN_HEIGHT - 90) //390 +UINT16 CHATLOG_SCROLL_AREA_END_Y; //(SCREEN_HEIGHT - 32) //448 +UINT16 CHATLOG_SCROLL_AREA_HEIGHT; //( CHATLOG_SCROLL_AREA_END_Y - CHATLOG_SCROLL_AREA_START_Y + 1 ) + +// CHRISL: Use these if we want scroll bar based on left edge of screen +UINT16 CHATLOG_SCROLL_AREA_START_X; //330 +UINT16 CHATLOG_SCROLL_AREA_END_X; //344 +UINT16 CHATLOG_SCROLL_AREA_WIDTH; //( CHATLOG_SCROLL_AREA_END_X - CHATLOG_SCROLL_AREA_START_X + 1 ) + +#define CHATLOG_BTN_SCROLL_TIME 100 + +#define CHAT_SLIDER_HEIGHT 11 +#define CHAT_SLIDER_WIDTH 11 + +#define MAX_CHATLOG_MESSAGES 8 + +UINT16 CHAT_SLIDER_BAR_RANGE; + +// buttons +UINT32 guiChatLogScrollButtons[ 2 ]; + +// buttons images +UINT32 guiChatLogScrollButtonsImage[ 2 ]; + +// mouse regions +MOUSE_REGION gChatLogScrollBarRegion; + +void LoadChatLogSliderBar( void ); +void DeleteChatLogSliderBar( void ); +void DisplayChatLogScrollBarSlider( ); +void CreateChatLogMessageScrollBarRegion( void ); +void DeleteChatLogMessageScrollRegion( void ); + +void ChatScreenMsgScrollDown( UINT8 ubLinesDown ); +void ChatScreenMsgScrollUp( UINT8 ubLinesUp ); +void ChangeCurrentChatScreenMessageIndex( UINT8 ubNewMessageIndex ); +void MoveToEndOfChatScreenMessageList( void ); + +void BtnMessageDownChatLogCallback( GUI_BUTTON *btn,INT32 reason ); +void BtnMessageUpChatLogCallback( GUI_BUTTON *btn,INT32 reason ); +void ChatScreenMessageScrollBarCallBack(MOUSE_REGION * pRegion, INT32 iReason ); +void EnableDisableChatLogScrollButtonsAndRegions( void ); + +// the local logical message index +UINT8 gubFirstChatLogMessageIndex = 0; + +// Chat Log Messages List +UINT8 gubStartOfChatLogMessageList = 0; // Front of the Message Queue +UINT8 gubEndOfChatLogMessageList = 0; // End of the Message Queue +UINT8 gubCurrentChatLogMessageString = 0; // // index of the current string we are looking at +static ScrollStringStPtr gChatLogMessageList[ 256 ]; // The message Queue + +void InitChatMessageList( void ); +void FreeChatMessageList( void ); +void DisplayStringsInChatLogMessageList( void ); +void AddStringToChatLogMessageList( STR16 pString, UINT16 usColor, UINT32 uiFont, BOOLEAN fStartOfNewString, UINT8 ubPriority ); +UINT8 GetRangeOfChatLogMessages( void ); + +void ClearWrappedStringsCHAT( WRAPPED_STRING *pStringWrapperHead ); + +#define CHAT_LINE_WIDTH 292 +#define CHAT_MESSAGE_FONT TINYFONT1 + + + + +INT32 DoChatBox( bool bIncludeChatLog, const STR16 zString, UINT32 uiExitScreen, MSGBOX_CALLBACK ReturnCallback, SGPRect *pCenteringRect ) +{ + VSURFACE_DESC vs_desc; + UINT16 usTextBoxWidth; + UINT16 usTextBoxHeight; + UINT16 usYMargin; + SGPRect aRect; + UINT32 uiDestPitchBYTES, uiSrcPitchBYTES; + UINT8 *pDestBuf, *pSrcBuf; + INT16 sButtonX, sButtonY, sBlankSpace; + UINT8 ubMercBoxBackground = BASIC_MERC_POPUP_BACKGROUND, ubMercBoxBorder = BASIC_MERC_POPUP_BORDER; + UINT8 ubFontColor, ubFontShadowColor; + UINT16 usCursor; + INT32 iId = -1; + + // clear the ouput string + memset(gszChatBoxInputString,0,sizeof(CHAR16)*255); + + gIncludeChatLog = bIncludeChatLog; + + GetMousePos( &pOldMousePosition ); + + if (bIncludeChatLog) + usYMargin = CHATBOX_Y_MARGIN_LOG; + else + usYMargin = CHATBOX_Y_MARGIN_NOLOG; + + //this variable can be unset if ur in a non gamescreen and DONT want the msg box to use the save buffer + gfDontOverRideSaveBuffer = TRUE; + + SetCurrentCursorFromDatabase( CURSOR_NORMAL ); + + if( gChatBox.BackRegion.uiFlags & MSYS_REGION_EXISTS ) + { + return( 0 ); + } + + // set style + ubMercBoxBackground = DIALOG_MERC_POPUP_BACKGROUND; + ubMercBoxBorder = DIALOG_MERC_POPUP_BORDER; + + // Add button images + gChatBox.iButtonImages = LoadButtonImage( "INTERFACE\\popupbuttons.sti", -1,0,-1,1,-1 ); + ubFontColor = CHATBOX_FONT_COLOR; + ubFontShadowColor = DEFAULT_SHADOW; + usCursor = CURSOR_NORMAL; + + + + + // Use default! + aRect.iTop = 0; + aRect.iLeft = 0; + aRect.iBottom = SCREEN_HEIGHT; + aRect.iRight = SCREEN_WIDTH; + + // Set some values! + //gChatBox.usFlags = usFlags; + gChatBox.uiExitScreen = uiExitScreen; + gChatBox.ExitCallback = ReturnCallback; + gChatBox.fRenderBox = TRUE; + gChatBox.bHandled = 0; + + // Init message box + if (bIncludeChatLog) + // we need a string just long enough to give 1 line, but max length of the box, we render the chatlog over this string so well never see it. DONT DELETE ANY SPACES + gChatBox.iBoxId = PrepareMercPopupBox( iId, ubMercBoxBackground, ubMercBoxBorder, L"A string that will be hidden, ", CHATBOX_WIDTH, CHATBOX_X_MARGIN, usYMargin, CHATBOX_Y_MARGIN_BOTTOM, &usTextBoxWidth, &usTextBoxHeight ); + else + gChatBox.iBoxId = PrepareMercPopupBox( iId, ubMercBoxBackground, ubMercBoxBorder, zString, CHATBOX_WIDTH, CHATBOX_X_MARGIN, usYMargin, CHATBOX_Y_MARGIN_BOTTOM, &usTextBoxWidth, &usTextBoxHeight ); + + if( gChatBox.iBoxId == -1 ) + { + #ifdef JA2BETAVERSION + AssertMsg( 0, "Failed in DoMessageBox(). Probable reason is because the string was too large to fit in max message box size." ); + #endif + return 0; + } + + // Save height,width + gChatBox.usWidth = usTextBoxWidth; + gChatBox.usHeight = usTextBoxHeight; + + // Determine position ( centered in rect ) + gChatBox.sX = (INT16)( ( ( ( aRect.iRight - aRect.iLeft ) - usTextBoxWidth ) / 2 ) + aRect.iLeft ); + gChatBox.sY = (INT16)( ( ( ( aRect.iBottom - aRect.iTop ) - usTextBoxHeight ) / 2 ) + aRect.iTop ); + + if ( guiCurrentScreen == GAME_SCREEN ) + { + gfStartedFromGameScreen = TRUE; + } + + if ( (fInMapMode == TRUE ) ) + { +// fMapExitDueToMessageBox = TRUE; + gfStartedFromMapScreen = TRUE; + fMapPanelDirty = TRUE; + } + + + // Set pending screen + SetPendingNewScreen( MP_CHAT_SCREEN); + + // Init save buffer + vs_desc.fCreateFlags = VSURFACE_CREATE_DEFAULT | VSURFACE_SYSTEM_MEM_USAGE; + vs_desc.usWidth = usTextBoxWidth; + vs_desc.usHeight = usTextBoxHeight; + vs_desc.ubBitDepth = 16; + + if( AddVideoSurface( &vs_desc, &gChatBox.uiSaveBuffer) == FALSE ) + { + return( - 1 ); + } + + //Save what we have under here... + pDestBuf = LockVideoSurface( gChatBox.uiSaveBuffer, &uiDestPitchBYTES); + pSrcBuf = LockVideoSurface( FRAME_BUFFER, &uiSrcPitchBYTES); + + Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES, + (UINT16 *)pSrcBuf, uiSrcPitchBYTES, + 0 , 0, + gChatBox.sX , gChatBox.sY, + usTextBoxWidth, usTextBoxHeight ); + + UnLockVideoSurface( gChatBox.uiSaveBuffer ); + UnLockVideoSurface( FRAME_BUFFER ); + + // Create top-level mouse region + MSYS_DefineRegion( &(gChatBox.BackRegion), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST, + usCursor, MSYS_NO_CALLBACK, ChatBoxClickCallback ); + + // Add region + MSYS_AddRegion(&(gChatBox.BackRegion) ); + + // findout if cursor locked, if so, store old params and store, restore when done + if( IsCursorRestricted() ) + { + fCursorLockedToArea = TRUE; + GetRestrictedClipCursor( &ChatBoxRestrictedCursorRegion ); + FreeMouseCursor( ); + } + + // vars for positioning controls on the chatbox + int usPosX = 0; + int usPosY = gChatBox.sY + GetFontHeight(CHATBOX_FONT_TITLE) + CHATBOX_Y_GAP + 5; + + if (bIncludeChatLog) + { + // CREATE BUTTONS AND IMAGES FOR CHATLOG + + VOBJECT_DESC VObjectDesc; + // will create buttons for interface bottom + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "INTERFACE\\mpchatbox.sti", VObjectDesc.ImageFile ); + if( !AddVideoObject( &VObjectDesc, &guiCHATLOGIMG ) ) + Assert( false ); + + gChatMessageLogRegion.iTop = usPosY; + gChatMessageLogRegion.iLeft = gChatBox.sX + (CHATBOX_X_MARGIN / 2); + gChatMessageLogRegion.iBottom = usPosY + CHATBOX_LOG_HEIGHT; + gChatMessageLogRegion.iRight = gChatMessageLogRegion.iLeft + CHATBOX_LOG_WIDTH; + + // SETUP SCROLLING AREA BOUNDS + CHATLOG_SCROLL_AREA_START_Y = gChatMessageLogRegion.iTop+20; + CHATLOG_SCROLL_AREA_END_Y = gChatMessageLogRegion.iBottom-20; + CHATLOG_SCROLL_AREA_HEIGHT = ( CHATLOG_SCROLL_AREA_END_Y - CHATLOG_SCROLL_AREA_START_Y + 1 ); + + CHATLOG_SCROLL_AREA_START_X = gChatMessageLogRegion.iRight + CHATBOX_SLIDER_GAP + 1; + CHATLOG_SCROLL_AREA_END_X = gChatMessageLogRegion.iRight + CHATBOX_SLIDER_GAP + 1 + CHAT_SLIDER_WIDTH; + CHATLOG_SCROLL_AREA_WIDTH = ( CHATLOG_SCROLL_AREA_END_X - CHATLOG_SCROLL_AREA_START_X + 1 ); + + CHAT_SLIDER_BAR_RANGE = ( CHATLOG_SCROLL_AREA_HEIGHT - CHAT_SLIDER_HEIGHT ); + + LoadChatLogSliderBar(); + + // Load Scroll button images + guiChatLogScrollButtonsImage[ CHAT_SCROLL_MESSAGE_UP ]= LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,11,4,-1,6,-1 ); + + guiChatLogScrollButtonsImage[ CHAT_SCROLL_MESSAGE_DOWN ]= LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,12,5,-1,7,-1 ); + + // Create buttons + guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_UP ] = QuickCreateButton( guiChatLogScrollButtonsImage[ CHAT_SCROLL_MESSAGE_UP ], gChatMessageLogRegion.iRight + CHATBOX_SLIDER_GAP + 2 , gChatMessageLogRegion.iTop + 1 , + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + (GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnMessageUpChatLogCallback); + guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_DOWN ] = QuickCreateButton( guiChatLogScrollButtonsImage[ CHAT_SCROLL_MESSAGE_DOWN ], gChatMessageLogRegion.iRight + CHATBOX_SLIDER_GAP + 2, gChatMessageLogRegion.iBottom - 16, + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST, + (GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnMessageDownChatLogCallback); + + + + usPosY = gChatBox.sY + CHATBOX_Y_MARGIN_LOG + (CHATBOX_Y_GAP * 2) + GetFontHeight(FONT12ARIAL) + 5; + // END CREATE CHATLOG + } + else + usPosY = gChatBox.sY + CHATBOX_Y_MARGIN_NOLOG + (CHATBOX_Y_GAP * 2) + GetFontHeight(FONT12ARIAL); + + // get the middle of the box + UINT16 middleBox = ( usTextBoxWidth / 2 ); + + // CREATE SEND TO ALLIES / ALL TOGGLES + // send to all + int toggleWidth = 32 + StringPixLength( gzMPChatToggleText[ 0 ], CHATBOX_FONT_TOGGLE ); + usPosX = gChatBox.sX + ((middleBox - toggleWidth)/2); + + guiChatToggles[ 0 ] = CreateCheckBoxButton( usPosX, usPosY, + "INTERFACE\\OptionsCheckBoxes_12x12.sti", MSYS_PRIORITY_HIGHEST, + BtnChatTogglesCallback ); + MSYS_SetBtnUserData( guiChatToggles[ 0 ], 0, 0 ); + + // send to allies + toggleWidth = 32 + StringPixLength( gzMPChatToggleText[ 1 ], CHATBOX_FONT_TOGGLE ); + usPosX = gChatBox.sX + middleBox + ((middleBox - toggleWidth)/2); + + + guiChatToggles[ 1 ] = CreateCheckBoxButton( usPosX, usPosY, + "INTERFACE\\OptionsCheckBoxes_12x12.sti", MSYS_PRIORITY_HIGHEST, + BtnChatTogglesCallback ); + MSYS_SetBtnUserData( guiChatToggles[ 1 ], 0, 1 ); + + usPosY += CHATBOX_TOGGLE_HEIGHT + CHATBOX_Y_GAP; + + // SET DEFAULT FLAGGED + + if (gbChatSendToAll) + ButtonList[ guiChatToggles[ 0 ] ]->uiFlags |= BUTTON_CLICKED_ON; + else + ButtonList[ guiChatToggles[ 1 ] ]->uiFlags |= BUTTON_CLICKED_ON; + + // END CREATE TOGGLES + + // CREATE TEXT INPUT BOX + InitTextInputMode(); // API call to initialise text input mode for this screen + // does not mean we are inputting text right away + + // Player Name field + SetTextInputCursor( CUROSR_IBEAM_WHITE ); + SetTextInputFont( (UINT16) FONT12ARIALFIXEDWIDTH ); //FONT12ARIAL //FONT12ARIALFIXEDWIDTH + Set16BPPTextFieldColor( Get16BPPColor(FROMRGB( 0, 0, 0) ) ); + SetBevelColors( Get16BPPColor(FROMRGB(136, 138, 135)), Get16BPPColor(FROMRGB(24, 61, 81)) ); + SetTextInputRegularColors( FONT_WHITE, 2 ); + SetTextInputHilitedColors( 2, FONT_WHITE, FONT_WHITE ); + SetCursorColor( Get16BPPColor(FROMRGB(255, 255, 255) ) ); + + usPosX = gChatBox.sX + (CHATBOX_X_MARGIN / 2); + //Add Player Name textbox + AddTextInputField( usPosX , + usPosY , + usTextBoxWidth - CHATBOX_X_MARGIN, + 20, + MSYS_PRIORITY_HIGH+2, + gszChatBoxInputString, + 255, + INPUTTYPE_ASCII );//23 + + gChatTextBoxRegion.iTop = usPosY; + gChatTextBoxRegion.iLeft = usPosX; + gChatTextBoxRegion.iBottom = usPosY+20; + gChatTextBoxRegion.iRight = usPosX+usTextBoxWidth - CHATBOX_X_MARGIN; + + // exit text input mode in this screen and clean up text boxes + SetActiveField( 0 ); + + usPosY += CHATBOX_INPUT_HEIGHT + CHATBOX_Y_GAP; + // END CREATE TEXT INPUT BOX + + // CREATE OK AND CANCEL BUTTONS + + // get the button width + UINT16 btnWidth = GetChatBoxButtonWidth( gChatBox.iButtonImages ); + + // Create OK Button + sButtonX = middleBox + ((middleBox - btnWidth)/2); + sButtonY = usTextBoxHeight - CHATBOX_BUTTON_HEIGHT - 10; + + gChatBox.uiOKButton = CreateIconAndTextButton( gChatBox.iButtonImages, pMessageStrings[ MSG_OK ], FONT12ARIAL, + CHATBOX_FONT_COLOR, ubFontShadowColor, + CHATBOX_FONT_COLOR, ubFontShadowColor, + TEXT_CJUSTIFIED, + (INT16)(gChatBox.sX + sButtonX ), (INT16)(gChatBox.sY + sButtonY ), BUTTON_TOGGLE ,MSYS_PRIORITY_HIGHEST, + DEFAULT_MOVE_CALLBACK, (GUI_CALLBACK)OKChatBoxCallback ); + SetButtonCursor(gChatBox.uiOKButton, usCursor); + ForceButtonUnDirty( gChatBox.uiOKButton ); + + // move the mouse over the ok button + if( gGameSettings.fOptions[ TOPTION_DONT_MOVE_MOUSE ] == FALSE ) + { + SimulateMouseMovement( ( gChatBox.sX + sButtonX + 27 ), ( gChatBox.sY + sButtonY + 10) ); + } + + // Create Cancel Button + sButtonX = ((middleBox - btnWidth)/2); + sButtonY = usTextBoxHeight - CHATBOX_BUTTON_HEIGHT - 10; + + gChatBox.uiNOButton = CreateIconAndTextButton( gChatBox.iButtonImages, pMessageStrings[ MSG_CANCEL ], FONT12ARIAL, + CHATBOX_FONT_COLOR, ubFontShadowColor, + CHATBOX_FONT_COLOR, ubFontShadowColor, + TEXT_CJUSTIFIED, + (INT16)(gChatBox.sX + sButtonX ), (INT16)(gChatBox.sY + sButtonY ), BUTTON_TOGGLE ,MSYS_PRIORITY_HIGHEST, + DEFAULT_MOVE_CALLBACK, (GUI_CALLBACK)CancelChatBoxCallback ); + SetButtonCursor(gChatBox.uiNOButton, usCursor); + ForceButtonUnDirty( gChatBox.uiNOButton ); + + // END CREATE BUTTONS + +#if 0 + gChatBox.fWasPaused = GamePaused(); + if (!gChatBox.fWasPaused) + { + InterruptTime(); + PauseGame(); + LockPauseState( 1 ); + // Pause timers as well.... + PauseTime( TRUE ); + } +#endif + + // Save mouse restriction region... + GetRestrictedClipCursor( &gOldCursorLimitRectangle ); + FreeMouseCursor( ); + + gfNewChatBox = TRUE; + + gfInChatBox = TRUE; + + return( iId ); +} + + +UINT32 ExitChatBox( INT8 ubExitCode ) +{ + UINT32 uiDestPitchBYTES, uiSrcPitchBYTES; + UINT8 *pDestBuf, *pSrcBuf; + SGPPoint pPosition; + + // Delete popup! + RemoveMercPopupBoxFromIndex( gChatBox.iBoxId ); + gChatBox.iBoxId = -1; + + + // OJW - 20090208 - Add text input box type + // exit text input mode in this screen and clean up text boxes + KillAllTextInputModes(); + + RemoveButton( gChatBox.uiOKButton ); + RemoveButton( gChatBox.uiNOButton ); + + // Delete button images + UnloadButtonImage( gChatBox.iButtonImages ); + + //Remove the toggle buttons + for(int cnt=0; cnt ChatBoxRestrictedCursorRegion.iRight ) || ( pPosition.iX > ChatBoxRestrictedCursorRegion.iLeft ) && ( pPosition.iY < ChatBoxRestrictedCursorRegion.iTop ) && ( pPosition.iY > ChatBoxRestrictedCursorRegion.iBottom ) ) + { + SimulateMouseMovement( pOldMousePosition.iX , pOldMousePosition.iY ); + } + + fCursorLockedToArea = FALSE; + RestrictMouseCursor( &ChatBoxRestrictedCursorRegion ); + } + + // Remove region + MSYS_RemoveRegion(&(gChatBox.BackRegion) ); + + // Remove save buffer! + DeleteVideoSurfaceFromIndex( gChatBox.uiSaveBuffer ); + + + switch( gChatBox.uiExitScreen ) + { + case GAME_SCREEN: + + if ( InOverheadMap( ) ) + { + gfOverheadMapDirty = TRUE; + } + else + { + SetRenderFlags( RENDER_FLAG_FULL ); + } + break; + case MAP_SCREEN: + fMapPanelDirty = TRUE; + break; + } + + if ( gfFadeInitialized ) + { + SetPendingNewScreen(FADE_SCREEN); + return( FADE_SCREEN ); + } + + return( gChatBox.uiExitScreen ); +} + +UINT32 MPChatScreenInit( ) +{ + InitChatMessageList(); + return( TRUE ); +} + + +UINT32 MPChatScreenHandle( ) +{ + InputAtom InputEvent; + + if ( gfNewChatBox ) + { + // If in game screen.... + if ( ( gfStartedFromGameScreen )||( gfStartedFromMapScreen ) ) + { + //UINT32 uiDestPitchBYTES, uiSrcPitchBYTES; + //UINT8 *pDestBuf, *pSrcBuf; + + if( gfStartedFromGameScreen ) + { + HandleTacticalUILoseCursorFromOtherScreen( ); + } + else + { + HandleMAPUILoseCursorFromOtherScreen( ); + } + + gfStartedFromGameScreen = FALSE; + gfStartedFromMapScreen = FALSE; + + } + + gfNewChatBox = FALSE; + + return( MP_CHAT_SCREEN ); + } + + + + UnmarkButtonsDirty( ); + + // Render the box! + if ( gChatBox.fRenderBox ) + { + + // Render the Background ( this includes the text string) + RenderMercPopUpBoxFromIndex( gChatBox.iBoxId, gChatBox.sX, gChatBox.sY, FRAME_BUFFER ); + + + UINT16 usWidth = StringPixLength( gzMPChatboxText[0], CHATBOX_FONT_TITLE ); + int usPosY = 0; + int usPosX = 0; + + usPosY = gChatBox.sY + 10; + usPosX = gChatBox.sX + ((gChatBox.usWidth - usWidth) / 2); + + DrawTextToScreen( gzMPChatboxText[0], usPosX, usPosY, usWidth, CHATBOX_FONT_TITLE, CHATBOX_FONT_COLOR, DEFAULT_SHADOW, FALSE, CENTER_JUSTIFIED | TEXT_SHADOWED ); + + // Render the toggle button strings + + + for(UINT8 cnt=0; cntXLoc + 12 + 10; + usPosY = btn->YLoc; + usWidth = StringPixLength( gzMPChatToggleText[ cnt ], CHATBOX_FONT_TOGGLE ); + + DrawTextToScreen( gzMPChatToggleText[ cnt ], usPosX, usPosY, usWidth, CHATBOX_FONT_TOGGLE, CHATBOX_FONT_COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + } + + if (gIncludeChatLog) + { + // draw chatbox + HVOBJECT hHandle; + // get and blt panel + GetVideoObject(&hHandle, guiCHATLOGIMG ); + BltVideoObject( FRAME_BUFFER , hHandle, 0, gChatMessageLogRegion.iLeft, gChatMessageLogRegion.iTop, VO_BLT_SRCTRANSPARENCY,NULL ); + BltVideoObject( FRAME_BUFFER , hHandle, 1, gChatMessageLogRegion.iRight+CHATBOX_SLIDER_GAP, gChatMessageLogRegion.iTop, VO_BLT_SRCTRANSPARENCY,NULL ); + // draw slider + DisplayChatLogScrollBarSlider( ); + // draw chat log text + DisplayStringsInChatLogMessageList(); + } + } + + MarkButtonsDirty(); + + EnableDisableChatLogScrollButtonsAndRegions(); + + // Render buttons + RenderButtons( ); + + // render text boxes + //SaveFontSettings(); + SetFontDestBuffer( FRAME_BUFFER, gChatTextBoxRegion.iLeft , gChatTextBoxRegion.iTop , gChatTextBoxRegion.iRight , gChatTextBoxRegion.iBottom, FALSE ); + RenderAllTextFields(); // textbox system call + SetFontDestBuffer( FRAME_BUFFER, 0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT , FALSE ); + //RestoreFontSettings(); + + + EndFrameBufferRender( ); + + // carter, need key shortcuts for clearing up message boxes + // Check for esc + while (DequeueEvent(&InputEvent) == TRUE) + { + if( !HandleTextInput( &InputEvent ) && InputEvent.usEvent == KEY_DOWN ) + { + if( ( InputEvent.usParam == ESC ) || ( InputEvent.usParam == 'n') ) + { + // Exit messagebox + gChatBox.bHandled = MSG_BOX_RETURN_NO; + memset(gszChatBoxInputString,0,sizeof(CHAR16)*255); + } + + if( InputEvent.usParam == ENTER ) + { + // retrieve the string from the text box + Get16BitStringFromField( 0, gszChatBoxInputString ); // these indexes are based on the order created + // Exit messagebox + gChatBox.bHandled = MSG_BOX_RETURN_OK; + } + + + } + } + + if ( gChatBox.bHandled ) + { + SetRenderFlags( RENDER_FLAG_FULL ); + return( ExitChatBox( gChatBox.bHandled ) ); + } + + return( MP_CHAT_SCREEN ); +} + +UINT32 MPChatScreenShutdown( ) +{ + FreeChatMessageList(); + return( FALSE ); +} + +UINT16 GetChatBoxButtonWidth( INT32 iButtonImage ) +{ + return( GetWidthOfButtonPic( (UINT16)iButtonImage, ButtonPictures[iButtonImage].OnNormal ) ); +} + +void CancelChatBoxCallback(GUI_BUTTON *btn, INT32 reason ) +{ + static BOOLEAN fLButtonDown = FALSE; + + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + fLButtonDown = TRUE; + } + else if( ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) && fLButtonDown ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + // OK, exit + gChatBox.bHandled = MSG_BOX_RETURN_NO; + // clear the ouput string + memset(gszChatBoxInputString,0,sizeof(CHAR16)*255); + } + else if ( reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) + { + fLButtonDown = FALSE; + } +} + +void OKChatBoxCallback(GUI_BUTTON *btn, INT32 reason ) +{ + static BOOLEAN fLButtonDown = FALSE; + + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + fLButtonDown = TRUE; + } + else if( ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) && fLButtonDown ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + // OK, exit + gChatBox.bHandled = MSG_BOX_RETURN_OK; + // retrieve the string from the text box + Get16BitStringFromField( 0, gszChatBoxInputString ); // these indexes are based on the order created + } + else if ( reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) + { + fLButtonDown = FALSE; + } + +} + +void BtnChatTogglesCallback( GUI_BUTTON *btn, INT32 reason ) +{ + UINT8 ubButton = (UINT8)MSYS_GetBtnUserData( btn, 0 ); + + if (ubButton == 1 && PLAYER_BSIDE != 1) + return; + + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + // disable all buttons in grp + UINT8 cnt; + for( cnt=0; cntuiFlags &= ~BUTTON_CLICKED_ON; + } + + // reselect the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + + gbChatSendToAll = ubButton == 0; // 0 is send to all + } + else + { + // check if any other buttons are checked + UINT8 cnt; + BOOLEAN fAnyChecked=FALSE; + for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) + { + fAnyChecked = TRUE; + } + } + + //if none are checked, re check this one + if( !fAnyChecked ) + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } + +} + +void RestoreChatToggleBackGrounds() +{ + UINT8 cnt; + + //Redraw toggle buttons + for( cnt=0; cntArea.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +void ChatBoxClickCallback( MOUSE_REGION * pRegion, INT32 iReason ) +{ + +} + +// SCROLLING FOR CHAT LOG + +void BtnMessageDownChatLogCallback( GUI_BUTTON *btn,INT32 reason ) +{ + static INT32 iLastRepeatScrollTime = 0; + + if( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + btn->uiFlags|=(BUTTON_CLICKED_ON); + + iLastRepeatScrollTime = 0; + } + else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + btn->uiFlags&=~(BUTTON_CLICKED_ON); + + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + // down a line + ChatScreenMsgScrollDown( 1 ); + } + } + else if( reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT ) + { + if( GetJA2Clock() - iLastRepeatScrollTime >= CHATLOG_BTN_SCROLL_TIME ) + { + // down a line + ChatScreenMsgScrollDown( 1 ); + + iLastRepeatScrollTime = GetJA2Clock( ); + } + } + else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN ) + { + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + btn->uiFlags|=(BUTTON_CLICKED_ON); + + iLastRepeatScrollTime = 0; + } + else if(reason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + btn->uiFlags&=~(BUTTON_CLICKED_ON); + + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + // down a page + ChatScreenMsgScrollDown( MAX_CHATLOG_MESSAGES ); + } + } + else if( reason & MSYS_CALLBACK_REASON_RBUTTON_REPEAT ) + { + if( GetJA2Clock() - iLastRepeatScrollTime >= CHATLOG_BTN_SCROLL_TIME ) + { + // down a page + ChatScreenMsgScrollDown( MAX_CHATLOG_MESSAGES ); + + iLastRepeatScrollTime = GetJA2Clock( ); + } + } +} + + +void BtnMessageUpChatLogCallback( GUI_BUTTON *btn,INT32 reason ) +{ + static INT32 iLastRepeatScrollTime = 0; + + + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags|=(BUTTON_CLICKED_ON); + + // redraw region + if( btn->Area.uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + iLastRepeatScrollTime = 0; + } + + else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + btn->uiFlags&=~(BUTTON_CLICKED_ON); + + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + // up a line + ChatScreenMsgScrollUp( 1 ); + } + } + else if( reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT ) + { + if( GetJA2Clock() - iLastRepeatScrollTime >= CHATLOG_BTN_SCROLL_TIME ) + { + // up a line + ChatScreenMsgScrollUp( 1 ); + + iLastRepeatScrollTime = GetJA2Clock( ); + } + } + else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN ) + { + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + btn->uiFlags|=(BUTTON_CLICKED_ON); + + iLastRepeatScrollTime = 0; + } + else if(reason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + btn->uiFlags&=~(BUTTON_CLICKED_ON); + + // redraw region + if( btn->uiFlags & MSYS_HAS_BACKRECT ) + { + gChatBox.fRenderBox = TRUE; + } + + // up a page + ChatScreenMsgScrollUp( MAX_CHATLOG_MESSAGES ); + } + } + else if( reason & MSYS_CALLBACK_REASON_RBUTTON_REPEAT ) + { + if( GetJA2Clock() - iLastRepeatScrollTime >= CHATLOG_BTN_SCROLL_TIME ) + { + // up a page + ChatScreenMsgScrollUp( MAX_CHATLOG_MESSAGES ); + + iLastRepeatScrollTime = GetJA2Clock( ); + } + } +} + +void EnableDisableChatLogScrollButtonsAndRegions( void ) +{ + UINT8 ubNumMessages; + + ubNumMessages = GetRangeOfChatLogMessages(); + + // if no scrolling required, or already showing the topmost message + if( ( ubNumMessages <= MAX_CHATLOG_MESSAGES ) || ( gubFirstChatLogMessageIndex == 0 ) ) + { + DisableButton( guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_UP ] ); + ButtonList[ guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_UP ] ]->uiFlags &= ~( BUTTON_CLICKED_ON ); + } + else + { + EnableButton( guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_UP ] ); + } + + // if no scrolling required, or already showing the last message + if( ( ubNumMessages <= MAX_CHATLOG_MESSAGES ) || + ( ( gubFirstChatLogMessageIndex + MAX_CHATLOG_MESSAGES ) >= ubNumMessages ) ) + { + DisableButton( guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_DOWN ] ); + ButtonList[ guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_DOWN ] ]->uiFlags &= ~( BUTTON_CLICKED_ON ); + } + else + { + EnableButton( guiChatLogScrollButtons[ CHAT_SCROLL_MESSAGE_DOWN ] ); + } + + if( ubNumMessages <= MAX_CHATLOG_MESSAGES ) + { + MSYS_DisableRegion( &gChatLogScrollBarRegion ); + } + else + { + MSYS_EnableRegion( &gChatLogScrollBarRegion ); + } + + if (PLAYER_BSIDE==1) + { + // Only enable Allies toggle for team deathmatch + EnableButton( guiChatToggles[1] ); + } + else + { + gbChatSendToAll = true; + DisableButton( guiChatToggles[1] ); + } +} + +void ChatScreenMsgScrollDown( UINT8 ubLinesDown ) +{ + UINT8 ubNumMessages; + + ubNumMessages = GetRangeOfChatLogMessages(); + + // check if we can go that far, only go as far as we can + if ( ( gubFirstChatLogMessageIndex + MAX_CHATLOG_MESSAGES + ubLinesDown ) > ubNumMessages ) + { + ubLinesDown = ubNumMessages - gubFirstChatLogMessageIndex - min( ubNumMessages, MAX_CHATLOG_MESSAGES ); + } + + if ( ubLinesDown > 0 ) + { + ChangeCurrentChatScreenMessageIndex( ( UINT8 ) ( gubFirstChatLogMessageIndex + ubLinesDown ) ); + } +} + + +void ChatScreenMsgScrollUp( UINT8 ubLinesUp ) +{ + UINT8 ubNumMessages; + + ubNumMessages = GetRangeOfChatLogMessages(); + + // check if we can go that far, only go as far as we can + if ( gubFirstChatLogMessageIndex < ubLinesUp ) + { + ubLinesUp = gubFirstChatLogMessageIndex; + } + + if ( ubLinesUp > 0 ) + { + ChangeCurrentChatScreenMessageIndex( ( UINT8 ) ( gubFirstChatLogMessageIndex - ubLinesUp ) ); + } +} + +void MoveToEndOfChatScreenMessageList( void ) +{ + UINT8 ubDesiredMessageIndex; + UINT8 ubNumMessages; + + ubNumMessages = GetRangeOfChatLogMessages(); + + ubDesiredMessageIndex = ubNumMessages - min( ubNumMessages, MAX_CHATLOG_MESSAGES ); + ChangeCurrentChatScreenMessageIndex( ubDesiredMessageIndex ); +} + + + +void ChangeCurrentChatScreenMessageIndex( UINT8 ubNewMessageIndex ) +{ + Assert( ubNewMessageIndex + MAX_CHATLOG_MESSAGES <= max( MAX_CHATLOG_MESSAGES, GetRangeOfChatLogMessages() ) ); + + gubFirstChatLogMessageIndex = ubNewMessageIndex; + gubCurrentChatLogMessageString = ( gubStartOfChatLogMessageList + gubFirstChatLogMessageIndex ) % 256; + + // set fact we just went to a new message +// gfNewScrollMessage = TRUE; + + // refresh screen + gChatBox.fRenderBox = TRUE; +} + +void LoadChatLogSliderBar( void ) +{ + // this function will load the message slider bar + VOBJECT_DESC VObjectDesc; + + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + FilenameForBPP( "INTERFACE\\map_screen_bottom_arrows.sti", VObjectDesc.ImageFile ); + if( !AddVideoObject( &VObjectDesc, &guiChatSliderBar ) ) + Assert(false); + CreateChatLogMessageScrollBarRegion(); +} + +void DeleteChatLogSliderBar( void ) +{ + // this function will delete message slider bar + DeleteVideoObjectFromIndex( guiChatSliderBar ); + + DeleteChatLogMessageScrollRegion(); +} + + +void CreateChatLogMessageScrollBarRegion( void ) +{ + MSYS_DefineRegion( &gChatLogScrollBarRegion, CHATLOG_SCROLL_AREA_START_X, CHATLOG_SCROLL_AREA_START_Y, + CHATLOG_SCROLL_AREA_END_X, CHATLOG_SCROLL_AREA_END_Y, + MSYS_PRIORITY_HIGHEST, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ChatScreenMessageScrollBarCallBack ); +} + + +void DeleteChatLogMessageScrollRegion( void ) +{ + MSYS_RemoveRegion( &gChatLogScrollBarRegion ); +} + + + +void ChatScreenMessageScrollBarCallBack( MOUSE_REGION *pRegion, INT32 iReason ) +{ + POINT MousePos; + UINT8 ubMouseYOffset; + UINT8 ubDesiredSliderOffset; + UINT8 ubDesiredMessageIndex; + UINT8 ubNumMessages; + + + if (iReason & MSYS_CALLBACK_REASON_INIT) + { + return; + } + + + if ( iReason & ( MSYS_CALLBACK_REASON_LBUTTON_DWN | MSYS_CALLBACK_REASON_LBUTTON_REPEAT ) ) + { + // how many messages are there? + ubNumMessages = GetRangeOfChatLogMessages(); + + // region is supposed to be disabled if there aren't enough messages to scroll. Formulas assume this + if ( ubNumMessages > MAX_CHATLOG_MESSAGES ) + { + // where is the mouse? + GetCursorPos( &MousePos ); + ScreenToClient(ghWindow, &MousePos); // In window coords! + + ubMouseYOffset = (UINT8) MousePos.y - CHATLOG_SCROLL_AREA_START_Y; + + // if clicking in the top 5 pixels of the slider bar + if ( ubMouseYOffset < ( CHAT_SLIDER_HEIGHT / 2 ) ) + { + // scroll all the way to the top + ubDesiredMessageIndex = 0; + } + // if clicking in the bottom 6 pixels of the slider bar + else if ( ubMouseYOffset >= ( CHATLOG_SCROLL_AREA_HEIGHT - ( CHAT_SLIDER_HEIGHT / 2 ) ) ) + { + // scroll all the way to the bottom + ubDesiredMessageIndex = ubNumMessages - MAX_CHATLOG_MESSAGES; + } + else + { + // somewhere in between + ubDesiredSliderOffset = ubMouseYOffset - ( CHAT_SLIDER_HEIGHT / 2 ); + + Assert( ubDesiredSliderOffset <= CHAT_SLIDER_BAR_RANGE ); + + // calculate what the index should be to place the slider at this offset (round fractions of .5+ up) + ubDesiredMessageIndex = ( ( ubDesiredSliderOffset * ( ubNumMessages - MAX_CHATLOG_MESSAGES ) ) + ( CHAT_SLIDER_BAR_RANGE / 2 ) ) / CHAT_SLIDER_BAR_RANGE; + } + + // if it's a change + if ( ubDesiredMessageIndex != gubFirstChatLogMessageIndex ) + { + ChangeCurrentChatScreenMessageIndex( ubDesiredMessageIndex ); + } + } + } +} + + +void DisplayChatLogScrollBarSlider( ) +{ + // will display the scroll bar icon + UINT8 ubNumMessages; + UINT8 ubSliderOffset; + HVOBJECT hHandle; + + + ubNumMessages = GetRangeOfChatLogMessages(); + + // only show the slider if there are more messages than will fit on screen + if ( ubNumMessages > MAX_CHATLOG_MESSAGES ) + { + // calculate where slider should be positioned + ubSliderOffset = ( CHAT_SLIDER_BAR_RANGE * gubFirstChatLogMessageIndex ) / ( ubNumMessages - MAX_CHATLOG_MESSAGES ); + + GetVideoObject( &hHandle, guiChatSliderBar ); + BltVideoObject( FRAME_BUFFER, hHandle, 8, CHATLOG_SCROLL_AREA_START_X + 2, CHATLOG_SCROLL_AREA_START_Y + ubSliderOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + } +} + +// END MESSAGE SCROLLING + +// CHAT MESSAGE ADDING AND PRINTING FUNCTIONS + +void ChatLogMessage( UINT16 usColor, UINT8 ubPriority, STR16 pStringA, ... ) +{ + // this function sets up the string into several single line structures + + //ScrollStringStPtr pStringSt; + UINT32 uiFont = CHAT_MESSAGE_FONT; + //STR16pString; + va_list argptr; + CHAR16 DestString[512]; + WRAPPED_STRING *pStringWrapper=NULL; + WRAPPED_STRING *pStringWrapperHead=NULL; + BOOLEAN fNewString = FALSE; + UINT16 usLineWidthIfWordIsWiderThenWidth; + + + /*pStringSt=pStringS; + while(GetNextString(pStringSt)) + pStringSt=GetNextString(pStringSt);*/ + + va_start(argptr, pStringA); // Set up variable argument pointer + vswprintf(DestString, pStringA, argptr); // process gprintf string (get output str) + va_end(argptr); + + // send message to tactical screen and map screen + ScreenMsg( usColor, ubPriority, DestString ); + + pStringWrapperHead=LineWrap(uiFont, CHAT_LINE_WIDTH, &usLineWidthIfWordIsWiderThenWidth, DestString); + pStringWrapper=pStringWrapperHead; + if(!pStringWrapper) + return; + + fNewString = TRUE; + + while(pStringWrapper->pNextWrappedString!=NULL) + { + AddStringToChatLogMessageList(pStringWrapper->sString, usColor, uiFont, fNewString, ubPriority ); + fNewString = FALSE; + + pStringWrapper=pStringWrapper->pNextWrappedString; + } + + AddStringToChatLogMessageList(pStringWrapper->sString, usColor, uiFont, fNewString, ubPriority ); + + + // clear up list of wrapped strings + ClearWrappedStringsCHAT( pStringWrapperHead ); + + // play new message beep + //PlayNewMessageSound( ); + + MoveToEndOfChatScreenMessageList( ); + + //LeaveMutex(SCROLL_MESSAGE_MUTEX, __LINE__, __FILE__); +} + + + +// add string to the chat log message list +void AddStringToChatLogMessageList( STR16 pString, UINT16 usColor, UINT32 uiFont, BOOLEAN fStartOfNewString, UINT8 ubPriority ) +{ + ScrollStringStPtr pStringSt = NULL; + + + pStringSt = (ScrollStringStPtr) MemAlloc(sizeof(ScrollStringSt)); + + SetString(pStringSt, pString); + SetStringColor(pStringSt, usColor); + pStringSt->uiFont = uiFont; + pStringSt->fBeginningOfNewString = fStartOfNewString; + pStringSt->uiFlags = ubPriority; + pStringSt->iVideoOverlay = -1; + + // next/previous are not used, it's strictly a wraparound queue + SetStringNext(pStringSt, NULL); + SetStringPrev(pStringSt, NULL); + + + // Figure out which queue slot index we're going to use to store this + // If queue isn't full, this is easy, if is is full, we'll re-use the oldest slot + // Must always keep the wraparound in mind, although this is easy enough with a static, fixed-size queue. + + + // always store the new message at the END index + + // check if slot is being used, if so, clear it up + if( gChatLogMessageList[ gubEndOfChatLogMessageList ] != NULL ) + { + MemFree( gChatLogMessageList[ gubEndOfChatLogMessageList ]->pString16 ); + MemFree( gChatLogMessageList[ gubEndOfChatLogMessageList ] ); + } + + // store the new message there + gChatLogMessageList[ gubEndOfChatLogMessageList ] = pStringSt; + + // increment the end + gubEndOfChatLogMessageList = ( gubEndOfChatLogMessageList + 1 ) % 256; + + // if queue is full, end will now match the start + if ( gubEndOfChatLogMessageList == gubStartOfChatLogMessageList ) + { + // if that's so, increment the start + gubStartOfChatLogMessageList = ( gubStartOfChatLogMessageList + 1 ) % 256; + } +} + + +void DisplayStringsInChatLogMessageList( void ) +{ + UINT8 ubCurrentStringIndex; + UINT8 ubLinesPrinted; + INT16 sX, sY; + UINT16 usSpacing; + + // Limit drawing to chat log region only, dont want any overdraw + sX = gChatMessageLogRegion.iLeft + 4; + SetFontDestBuffer( FRAME_BUFFER, sX , gChatMessageLogRegion.iTop + 4, gChatMessageLogRegion.iRight - 4, gChatMessageLogRegion.iBottom - 4, FALSE ); + + SetFont( CHAT_MESSAGE_FONT ); // no longer supports variable fonts + SetFontBackground( FONT_BLACK ); + SetFontShadow( DEFAULT_SHADOW ); + + ubCurrentStringIndex = gubCurrentChatLogMessageString; + + sY = gChatMessageLogRegion.iTop + 4; + + usSpacing = GetFontHeight( CHAT_MESSAGE_FONT ); + + for ( ubLinesPrinted = 0; ubLinesPrinted < MAX_CHATLOG_MESSAGES; ubLinesPrinted++ ) + { + // reached the end of the list? + if ( ubCurrentStringIndex == gubEndOfChatLogMessageList ) + { + break; + } + + // nothing stored there? + if ( gChatLogMessageList[ ubCurrentStringIndex ] == NULL ) + { + break; + } + + // set font color + SetFontForeground( ( UINT8 )( gChatLogMessageList[ ubCurrentStringIndex ]->usColor ) ); + + // print this line + mprintf_coded( sX, sY, gChatLogMessageList[ ubCurrentStringIndex ]->pString16 ); + + sY = sY + usSpacing; + + // next message index to print (may wrap around) + ubCurrentStringIndex = ( ubCurrentStringIndex + 1 ) % 256; + } + + // reset region to whole screen + SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE ); +} + +void InitChatMessageList( void ) +{ + INT32 iCounter = 0; + + for( iCounter = 0; iCounter < 256; iCounter++ ) + { + gChatLogMessageList[ iCounter ] = NULL; + } + + gubEndOfChatLogMessageList = 0; + gubStartOfChatLogMessageList = 0; + gubCurrentChatLogMessageString = 0; + + return; +} + + +void FreeChatMessageList( void ) +{ + INT32 iCounter = 0; + + for( iCounter = 0; iCounter < 256; iCounter++ ) + { + // check if next unit is empty, if not...clear it up + if( gChatLogMessageList[ iCounter ] != NULL ) + { + MemFree( gChatLogMessageList[ iCounter ]->pString16 ); + MemFree( gChatLogMessageList[ iCounter ] ); + } + } + + InitChatMessageList( ); + + return; +} + +UINT8 GetRangeOfChatLogMessages( void ) +{ + UINT8 ubRange = 0; + + // NOTE: End is non-inclusive, so start/end 0/0 means no messages, 0/1 means 1 message, etc. + if( gubStartOfChatLogMessageList <= gubEndOfChatLogMessageList ) + { + ubRange = gubEndOfChatLogMessageList - gubStartOfChatLogMessageList; + } + else + { + // this should always be 255 now, since this only happens when queue fills up, and we never remove any messages + ubRange = ( gubEndOfChatLogMessageList + 256 ) - gubStartOfChatLogMessageList; + } + + return ( ubRange ); +} + +// helper function to clear a list of wrapped strings +// This same function exists in message.cpp +// copied here because i didnt want to include wordwrap.h in message.h +void ClearWrappedStringsCHAT( WRAPPED_STRING *pStringWrapperHead ) +{ + WRAPPED_STRING *pNode = pStringWrapperHead; + WRAPPED_STRING *pDeleteNode = NULL; + // clear out a link list of wrapped string structures + + // error check, is there a node to delete? + if( pNode == NULL ) + { + // leave, + return; + } + + do + { + + // set delete node as current node + pDeleteNode = pNode; + + // set current node as next node + pNode = pNode->pNextWrappedString; + + //delete the string + MemFree( pDeleteNode->sString ); + pDeleteNode->sString = NULL; + + // clear out delete node + MemFree( pDeleteNode ); + pDeleteNode = NULL; + + } while( pNode ); + + +// MemFree( pNode ); + + pStringWrapperHead = NULL; + +} \ No newline at end of file diff --git a/MPChatScreen.h b/MPChatScreen.h new file mode 100644 index 00000000..6fee1aee --- /dev/null +++ b/MPChatScreen.h @@ -0,0 +1,27 @@ +#ifndef _MP_CHAT_SCREEN_H_ +#define _MP_CHAT_SCREEN_H_ + +#include "MessageBoxScreen.h" + +UINT32 MPChatScreenInit( void ); +UINT32 MPChatScreenHandle( void ); +UINT32 MPChatScreenShutdown( void ); + +extern CHAR16 gszChatBoxInputString[255]; +extern BOOLEAN gbChatSendToAll; +extern MESSAGE_BOX_STRUCT gChatBox; +extern BOOLEAN gfInChatBox; + +//////////////////////////////// +// ubStyle: Determines the look of graphics including buttons +// zString: 16-bit string +// uiExitScreen The screen to exit to +// ubFlags Some flags for button style +// ReturnCallback Callback for return. Can be NULL. Returns any above return value +// pCenteringRect Rect to send if MSG_BOX_FLAG_USE_CENTERING_RECT set. Can be NULL +//////////////////////////////// + +INT32 DoChatBox( bool bIncludeChatLog, const STR16 zString, UINT32 uiExitScreen, MSGBOX_CALLBACK ReturnCallback, SGPRect *pCenteringRect ); +void ChatLogMessage( UINT16 usColor, UINT8 ubPriority, STR16 pStringA, ...); + +#endif \ No newline at end of file diff --git a/MPHostScreen.cpp b/MPHostScreen.cpp new file mode 100644 index 00000000..1f1a51be --- /dev/null +++ b/MPHostScreen.cpp @@ -0,0 +1,1507 @@ +#ifdef PRECOMPILEDHEADERS + #include "JA2 All.h" + #include "Intro.h" +#else + #include "Types.h" + #include "MPHostScreen.h" + #include "GameSettings.h" + #include "Utilities.h" + #include "wCheck.h" + #include "Font Control.h" + #include "WordWrap.h" + #include "Render Dirty.h" + #include "Input.h" + #include "Options Screen.h" + #include "English.h" + #include "Sysutil.h" + #include "Fade Screen.h" + #include "Cursor Control.h" + #include "Music Control.h" + #include "cursors.h" + #include "Intro.h" + #include "Text.h" + #include "Text Input.h" + #include "_Ja25EnglishText.h" + #include "Soldier Profile.h" +#endif + +#include "gameloop.h" +#include "connect.h" +#include "network.h" // for client name +#include "saveloadscreen.h" + + +//////////////////////////////////////////// +// +// Global Defines +// +/////////////////////////////////////////// + +#define MPH_TITLE_FONT FONT14ARIAL//FONT16ARIAL +#define MPH_TITLE_COLOR FONT_MCOLOR_WHITE + +#define MPH_LABEL_TEXT_FONT FONT12ARIAL//FONT16ARIAL +#define MPH_LABEL_TEXT_COLOR FONT_MCOLOR_WHITE +#define MPH_TOGGLE_TEXT_FONT FONT12ARIAL//FONT16ARIAL +#define MPH_TOGGLE_TEXT_COLOR FONT_MCOLOR_WHITE + +//buttons +#define MPH_BTN_START_X iScreenWidthOffset + 320 + 143 +#define MPH_BTN_START_Y iScreenHeightOffset + 435 +#define MPH_CANCEL_X iScreenWidthOffset + ((320 - 115) / 2) + +//textboxes +#define MPH_TXT_SVRNAME_X iScreenWidthOffset + 100 +#define MPH_TXT_SVRNAME_Y iScreenHeightOffset + 167 +#define MPH_TXT_SVRNAME_WIDTH 120 +#define MPH_TXT_SVRNAME_HEIGHT 17 +#define MPH_TXT_MAXPLAYERS_X iScreenWidthOffset + 100 +#define MPH_TXT_MAXPLAYERS_Y MPH_TXT_SVRNAME_Y + MPH_TXT_SVRNAME_HEIGHT + 13 +#define MPH_TXT_MAXPLAYERS_WIDTH 40 +#define MPH_TXT_MAXPLAYERS_HEIGHT 17 +#define MPH_TXT_SQUAD_X iScreenWidthOffset + 100 +#define MPH_TXT_SQUAD_Y MPH_TXT_MAXPLAYERS_Y + MPH_TXT_MAXPLAYERS_HEIGHT + 13 +#define MPH_TXT_SQUAD_WIDTH 40 +#define MPH_TXT_SQUAD_HEIGHT 17 +#define MPH_TXT_TIME_X iScreenWidthOffset + 100 +#define MPH_TXT_TIME_Y MPH_TXT_SQUAD_Y + MPH_TXT_SQUAD_HEIGHT + 13 +#define MPH_TXT_TIME_WIDTH 60 +#define MPH_TXT_TIME_HEIGHT 17 +#define MPH_TXT_CASH_X iScreenWidthOffset + 100 +#define MPH_TXT_CASH_Y MPH_TXT_TIME_Y + MPH_TXT_TIME_HEIGHT + 13 +#define MPH_TXT_CASH_WIDTH 60 +#define MPH_TXT_CASH_HEIGHT 17 +#define MPH_TXT_DMG_X iScreenWidthOffset + 100 +#define MPH_TXT_DMG_Y MPH_TXT_CASH_Y + MPH_TXT_CASH_HEIGHT + 13 +#define MPH_TXT_DMG_WIDTH 60 +#define MPH_TXT_DMG_HEIGHT 17 +#define MPH_TXT_TIMER_X iScreenWidthOffset + 100 +#define MPH_TXT_TIMER_Y MPH_TXT_DMG_Y + MPH_TXT_DMG_HEIGHT + 13 +#define MPH_TXT_TIMER_WIDTH 60 +#define MPH_TXT_TIMER_HEIGHT 17 + + + +//main title +#define MPH_MAIN_TITLE_X 0 +#define MPH_MAIN_TITLE_Y iScreenHeightOffset + 10 +#define MPH_MAIN_TITLE_WIDTH SCREEN_WIDTH + +//labels +#define MPH_LABEL_SVRNAME_X MPH_TXT_SVRNAME_X - 80 +#define MPH_LABEL_SVRNAME_Y MPH_TXT_SVRNAME_Y + 3 +#define MPH_LABEL_SVRNAME_WIDTH 80 +#define MPH_LABEL_SVRNAME_HEIGHT 17 +#define MPH_LABEL_MAXPLAYERS_X MPH_TXT_MAXPLAYERS_X - 80 +#define MPH_LABEL_MAXPLAYERS_Y MPH_TXT_MAXPLAYERS_Y + 3 +#define MPH_LABEL_MAXPLAYERS_WIDTH 80 +#define MPH_LABEL_MAXPLAYERS_HEIGHT 17 +#define MPH_LABEL_SQUAD_X MPH_TXT_SQUAD_X - 80 +#define MPH_LABEL_SQUAD_Y MPH_TXT_SQUAD_Y + 3 +#define MPH_LABEL_SQUAD_WIDTH 80 +#define MPH_LABEL_SQUAD_HEIGHT 17 +#define MPH_LABEL_TIME_X MPH_TXT_TIME_X - 80 +#define MPH_LABEL_TIME_Y MPH_TXT_TIME_Y + 3 +#define MPH_LABEL_TIME_WIDTH 80 +#define MPH_LABEL_TIME_HEIGHT 17 +#define MPH_LABEL_CASH_X MPH_TXT_CASH_X - 80 +#define MPH_LABEL_CASH_Y MPH_TXT_CASH_Y + 3 +#define MPH_LABEL_CASH_WIDTH 80 +#define MPH_LABEL_CASH_HEIGHT 17 +#define MPH_LABEL_DMG_X MPH_TXT_DMG_X - 80 +#define MPH_LABEL_DMG_Y MPH_TXT_DMG_Y + 3 +#define MPH_LABEL_DMG_WIDTH 80 +#define MPH_LABEL_DMG_HEIGHT 17 +#define MPH_LABEL_TIMER_X MPH_TXT_TIMER_X - 80 +#define MPH_LABEL_TIMER_Y MPH_TXT_TIMER_Y + 3 +#define MPH_LABEL_TIMER_WIDTH 80 +#define MPH_LABEL_TIMER_HEIGHT 17 + +//radio box locations +#define MPH_GAP_BN_SETTINGS 35 +#define MPH_OFFSET_TO_TEXT 30 +#define MPH_OFFSET_TO_TOGGLE_BOX 170 +#define MPH_OFFSET_TO_TOGGLE_BOX_Y 9 + +#define MPH_DIF_SETTINGS_X iScreenWidthOffset + 320 +#define MPH_DIF_SETTINGS_Y iScreenHeightOffset + 75 +#define MPH_DIF_SETTINGS_WIDTH MPH_OFFSET_TO_TOGGLE_BOX - MPH_OFFSET_TO_TEXT + +#define MPH_GAMETYPE_SETTINGS_X iScreenWidthOffset + 20 +#define MPH_GAMETYPE_SETTINGS_Y iScreenHeightOffset + 75 +#define MPH_GAMETYPE_SETTINGS_WIDTH MPH_OFFSET_TO_TOGGLE_BOX - MPH_OFFSET_TO_TEXT + +#define MPH_RNDMERC_X MPH_DIF_SETTINGS_X +#define MPH_RNDMERC_Y MPH_TXT_MAXPLAYERS_Y//iScreenHeightOffset+214 +#define MPH_RNDMERC_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_SAMEMERC_X MPH_DIF_SETTINGS_X +#define MPH_SAMEMERC_Y MPH_RNDMERC_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_SAMEMERC_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_REPORTMERC_X MPH_DIF_SETTINGS_X +#define MPH_REPORTMERC_Y MPH_SAMEMERC_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_REPORTMERC_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_BOBBYRAY_X MPH_DIF_SETTINGS_X +#define MPH_BOBBYRAY_Y MPH_REPORTMERC_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_BOBBYRAY_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_RNDSPAWN_X MPH_DIF_SETTINGS_X +#define MPH_RNDSPAWN_Y MPH_BOBBYRAY_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_RNDSPAWN_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_CIVS_X MPH_DIF_SETTINGS_X +#define MPH_CIVS_Y MPH_RNDSPAWN_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_CIVS_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +#define MPH_USENIV_X MPH_DIF_SETTINGS_X +#define MPH_USENIV_Y MPH_CIVS_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_USENIV_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + +//Difficulty settings +enum +{ + MPH_DIFF_EASY, + MPH_DIFF_MED, + MPH_DIFF_HARD, + MPH_DIFF_INSANE, + NUM_DIFF_SETTINGS, +}; + + + +//////////////////////////////////////////// +// +// Global Variables +// +/////////////////////////////////////////// + +BOOLEAN gfMPHScreenEntry = TRUE; +BOOLEAN gfMPHScreenExit = FALSE; +BOOLEAN gfReRenderMPHScreen=TRUE; +BOOLEAN gfMPHButtonsAllocated = FALSE; + +//enum for different states of screen +enum +{ + MPH_NOTHING, + MPH_CANCEL, + MPH_START +}; + +UINT8 gubMPHScreenHandler=MPH_NOTHING; // State changer for HandleMPHScreen() + +UINT32 gubMPHExitScreen = MP_HOST_SCREEN; // The screen that is in control next iteration of the game_loop + +UINT32 guiMPHMainBackGroundImage; + +// Wide-char strings that will hold the variables until they are transferred to the CHAR ascii fields +CHAR16 gzServerNameField[ 30 ] = {0} ; +CHAR16 gzMaxPlayersField[ 2 ] = {0} ; +CHAR16 gzSquadSizeField[ 2 ] = {0} ; +CHAR16 gzTimeOfDayField[ 5 ] = {0}; +CHAR16 gzStartingBalanceField[ 10 ] = {0}; +INT16 giMPHTimeHours = 0; +INT16 giMPHTimeMins = 0; +CHAR16 gzDmgMultiplierField[ 5 ] = {0}; +CHAR16 gzTimerField[ 5 ] = {0}; + +UINT8 guiMPHGameType = MP_TYPE_DEATHMATCH; // default value + +INT32 giMPHMessageBox = -1; // message box handle + +INT giMPHRandomMercs = 0; +INT giMPHSameMercs = 0; +INT giMPHReportMercs = 0; +INT giMPHBobbyRays = 0; +INT giMPHRandomSpawn = 0; +INT giMPHEnableCivilians = 0; +INT giMPHUseNIV = 0; + + +//////////////////////////////////////////// +// +// Screen Controls +// +/////////////////////////////////////////// + +// Start Button +void BtnMPHStartCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPHStartButton; +INT32 giMPHStartBtnImage; + +// Cancel Button +void BtnMPHCancelCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPHCancelButton; +INT32 giMPHCancelBtnImage; + +//checkbox to toggle the Diff level +UINT32 guiMPHDifficultySettingsToggles[ NUM_DIFF_SETTINGS ]; +void BtnMPHDifficultyTogglesCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox to toggle Game Type +UINT32 guiMPHGameTypeToggles[ NUM_MP_GAMETYPE ]; +void BtnMPHGameTypeTogglesCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for merc options +UINT32 guiMPHRandomMercsToggle; +void BtnMPHRandomMercCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for same merc +UINT32 guiMPHSameMercToggle; +void BtnMPHSameMercCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for report purchase +UINT32 guiMPHReportMercToggle; +void BtnMPHReportMercCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for bobby rays +UINT32 guiMPHBobbyRayToggle; +void BtnMPHBobbyRayCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for random spawn pos +UINT32 guiMPHRandomSpawnToggle; +void BtnMPHRandomSpawnCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for enable civilians in CO-OP +UINT32 guiMPHCivsToggle; +void BtnMPHCivsCallback(GUI_BUTTON *btn,INT32 reason); + +//checkbox for use NIV +UINT32 guiMPHUseNIVToggle; +void BtnMPHUseNIVCallback(GUI_BUTTON *btn,INT32 reason); + + +//////////////////////////////////////////// +// +// Local Function Prototypes +// +/////////////////////////////////////////// + +extern void ClearMainMenu(); + +BOOLEAN EnterMPHScreen(); +BOOLEAN ExitMPHScreen(); +void HandleMPHScreen(); +BOOLEAN RenderMPHScreen(); +void GetMPHScreenUserInput(); +void RestoreMPHButtonBackGrounds(); +bool ValidateMPSettings(); +void SaveMPSettings(); +UINT8 GetMPHCurrentDifficultyButtonSetting(); +UINT8 GetMPHGameTypeButtonSetting(); +BOOLEAN DoMPHMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ); +void DoneFadeOutForExitMPHScreen( void ); +void DoneFadeInForExitMPHScreen( void ); + +//////////////////////////////////////////// +// +// Functions +// +/////////////////////////////////////////// + +void SaveMPSettings() +{ + Get16BitStringFromField( 0, gzServerNameField ); // these indexes are based on the order created + Get16BitStringFromField( 1, gzMaxPlayersField ); + Get16BitStringFromField( 2, gzSquadSizeField ); + + // save settings to JA2_mp.ini + WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_NAME", gzServerNameField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"MAX_CLIENTS", gzMaxPlayersField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"MAX_MERCS", gzSquadSizeField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"STARTING_BALANCE", gzStartingBalanceField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"DAMAGE_MULTIPLIER", gzDmgMultiplierField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", gzTimerField, L"..\\Ja2_mp.ini" ); + + + guiMPHGameType = GetMPHGameTypeButtonSetting(); + CHAR16 tmpGTStr[2]; + _itow(guiMPHGameType,tmpGTStr,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"GAME_MODE", tmpGTStr, L"..\\Ja2_mp.ini" ); + + CHAR16 tmpTimeStr[6]; + swprintf(tmpTimeStr,L"%i.%i",giMPHTimeHours,giMPHTimeMins); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"TIME", tmpTimeStr, L"..\\Ja2_mp.ini" ); + + CHAR16 tmpVal[2]; + + giMPHRandomMercs = ( ButtonList[ guiMPHRandomMercsToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHRandomMercs,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_MERCS", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHSameMercs = ( ButtonList[ guiMPHSameMercToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHSameMercs,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"SAME_MERC", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHBobbyRays = ( ButtonList[ guiMPHBobbyRayToggle ]->uiFlags & BUTTON_CLICKED_ON ? 0 : 1 ); // This setting is reversed + _itow(giMPHBobbyRays,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"DISABLE_BOBBY_RAYS", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHReportMercs = ( ButtonList[ guiMPHReportMercToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHReportMercs,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"REPORT_NAME", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHRandomSpawn = ( ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHRandomSpawn,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_EDGES", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHEnableCivilians = ( ButtonList[ guiMPHCivsToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHEnableCivilians,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"CIV_ENABLED", tmpVal, L"..\\Ja2_mp.ini" ); + + giMPHUseNIV = ( ButtonList[ guiMPHUseNIVToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHUseNIV,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", tmpVal, L"..\\Ja2_mp.ini" ); + + + + + /*if ( ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags & BUTTON_CLICKED_ON ) + WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_EDGES", L"1", L"..\\Ja2_mp.ini" ); + else + WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_EDGES", L"0", L"..\\Ja2_mp.ini" );*/ + + // save game difficulty setting + gGameOptions.ubDifficultyLevel = GetMPHCurrentDifficultyButtonSetting(); +} + +bool ValidateMPSettings() +{ + // Check a Server name is entered + Get16BitStringFromField( 0, gzServerNameField ); // these indexes are based on the order created + if (wcscmp(gzServerNameField,L"")<=0) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_SERVERNAME_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // Verify the Max Players + Get16BitStringFromField( 1, gzMaxPlayersField ); + UINT8 numPlayers = _wtoi(gzMaxPlayersField); + if (numPlayers < 2 || numPlayers > 4) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_MAXPLAYERS_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // Verify the Squad Size + Get16BitStringFromField( 2, gzSquadSizeField ); + UINT8 squadSize = _wtoi(gzSquadSizeField); + if (squadSize < 1 || squadSize > 6) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_SQUADSIZE_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // verify the Time of Day + Get16BitStringFromField( 3, gzTimeOfDayField ); + wchar_t* tok; + bool bTimeOK = true; + int hours = 0; + int mins = 0; + + // strtok is destructive, make a copy to work on + CHAR16 tmpTODStr[5]; + memcpy(tmpTODStr,gzTimeOfDayField,sizeof(CHAR16)*5); + + tok = wcstok(tmpTODStr,L":"); + if (tok != NULL) + { + hours = _wtoi(tok); + // check for invalid conversion, ie alpha chars + // wtoi returns 0 if it cant convert, but we need this value + // therefore if tok <> 0 then it was a bad convert. + if (hours == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } + + tok = wcstok(NULL,L"."); + if (tok != NULL) + { + mins = _wtoi(tok); + if (mins == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } + + // fix for single digits + if (wcslen(tok) == 1) + mins = mins * 10; + } + else + { + bTimeOK = false; + } + } + else + { + bTimeOK = false; + } + + + if (bTimeOK) + { + giMPHTimeHours = hours; + float ratio = (float)mins / 60.0f; + giMPHTimeMins = (INT16)(ratio * 100); + + } + else + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_TIME_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + + // verify the Starting Balance + Get16BitStringFromField( 4, gzStartingBalanceField ); + UINT32 sBalance = _wtoi(gzStartingBalanceField); + if (sBalance < 1) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_CASH_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + Get16BitStringFromField( 5, gzDmgMultiplierField ); + double fDmg = _wtof(gzDmgMultiplierField); + if (fDmg <= 0.0f || fDmg >= 5.0f) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_DMG_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + Get16BitStringFromField( 6, gzTimerField ); + UINT32 iTimer = _wtoi(gzTimerField); + if (iTimer < 1 || iTimer > 200) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_TIMER_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + return true; +} + +UINT32 MPHostScreenInit( void ) +{ + // read settings from JA2_mp.ini + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_NAME", L"Mr Server", gzServerNameField, 30, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_CLIENTS", L"", gzMaxPlayersField, 2, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_MERCS", L"", gzSquadSizeField, 2 , L"..\\Ja2_mp.ini" ); + guiMPHGameType = GetPrivateProfileIntW( L"Ja2_mp Settings",L"GAME_MODE", MP_TYPE_DEATHMATCH, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"STARTING_BALANCE", L"9:30", gzStartingBalanceField, 10 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"DAMAGE_MULTIPLIER", L"0.7", gzDmgMultiplierField, 5 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", L"100", gzTimerField, 5 , L"..\\Ja2_mp.ini" ); + + giMPHRandomMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"RANDOM_MERCS", 0, L"..\\Ja2_mp.ini" ); + giMPHSameMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"SAME_MERC", 0, L"..\\Ja2_mp.ini" ); + giMPHReportMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"REPORT_NAME", 0, L"..\\Ja2_mp.ini" ); + giMPHBobbyRays = GetPrivateProfileIntW( L"Ja2_mp Settings",L"DISABLE_BOBBY_RAYS", 0, L"..\\Ja2_mp.ini" ); + giMPHRandomSpawn = GetPrivateProfileIntW( L"Ja2_mp Settings",L"RANDOM_EDGES", 0, L"..\\Ja2_mp.ini" ); + giMPHEnableCivilians = GetPrivateProfileIntW( L"Ja2_mp Settings",L"CIV_ENABLED", 0, L"..\\Ja2_mp.ini" ); + giMPHUseNIV = GetPrivateProfileIntW( L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", 0, L"..\\Ja2_mp.ini" ); + + + // read in time + CHAR16 szTime[5]; + GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIME", L"13.50", szTime, 5 , L"..\\Ja2_mp.ini" ); + + wchar_t* tok; + bool bTimeOK = true; + int hours = 0; + int mins = 0; + + tok = wcstok(szTime,L"."); + + if (tok != NULL) + { + hours = _wtoi(tok); + // check for invalid conversion, ie alpha chars + // wtoi returns 0 if it cant convert, but we need this value + // therefore if tok <> 0 then it was a bad convert. + if (hours == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } + + tok = wcstok(NULL,L"."); + if (tok != NULL) + { + mins = _wtoi(tok); + if (mins == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } + + // fix for single digits + if (wcslen(tok) == 1) + mins = mins * 10; + } + else + { + bTimeOK = false; + } + } + else + { + bTimeOK = false; + } + + + + if (bTimeOK) + { + giMPHTimeHours = hours; + float ratio = (float)mins / 100.0f; + giMPHTimeMins = (INT16)(ratio * 60); + swprintf(gzTimeOfDayField,L"%i:%i",giMPHTimeHours,giMPHTimeMins); + } + + return( 1 ); +} + + +UINT32 MPHostScreenHandle( void ) +{ + StartFrameBufferRender(); + + if( gfMPHScreenEntry ) + { +// PauseGame(); + + EnterMPHScreen(); + gfMPHScreenEntry = FALSE; + gfMPHScreenExit = FALSE; + InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + } + + GetMPHScreenUserInput(); + + + HandleMPHScreen(); + + // render buttons marked dirty + MarkButtonsDirty( ); + RenderButtons( ); + + // render text boxes + RenderAllTextFields(); // textbox system call + + // render help +// RenderFastHelp( ); +// RenderButtonsFastHelp( ); + + + ExecuteBaseDirtyRectQueue(); + EndFrameBufferRender(); + + // handle fades in and out + if ( HandleFadeOutCallback( ) ) + { + ClearMainMenu(); + return( gubMPHExitScreen ); + } + + if ( HandleBeginFadeOut( gubMPHExitScreen ) ) + { + return( gubMPHExitScreen ); + } + + if ( HandleFadeInCallback( ) ) + { + // Re-render the scene! + RenderMPHScreen(); + } + + if ( HandleBeginFadeIn( gubMPHExitScreen ) ) + { + + } + + if( gfMPHScreenExit ) // we are exiting this screen + { + ExitMPHScreen(); // perform destruction + } + + return( gubMPHExitScreen ); +} // end MPHoinScreenHandle() + + +UINT32 MPHostScreenShutdown( void ) +{ + return( 1 ); +} + +BOOLEAN EnterMPHScreen() +{ + VOBJECT_DESC VObjectDesc; + UINT16 cnt; + UINT16 usPosY; + + if( gfMPHButtonsAllocated ) + return( TRUE ); + + SetCurrentCursorFromDatabase( CURSOR_NORMAL ); + + // load the Main trade screen backgroiund image + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_800x600.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_1024x768.sti", VObjectDesc.ImageFile); + } + + CHECKF(AddVideoObject(&VObjectDesc, &guiMPHMainBackGroundImage )); + + //Join button + giMPHStartBtnImage = LoadButtonImage("INTERFACE\\PreferencesButtons.sti", -1,0,-1,2,-1 ); + guiMPHStartButton = CreateIconAndTextButton( giMPHStartBtnImage, gzMPHScreenText[MPH_START_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPH_BTN_START_X, MPH_BTN_START_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPHStartCallback); + + SpecifyButtonSoundScheme( guiMPHStartButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPHStartButton, DISABLED_STYLE_NONE ); + + //Cancel button + giMPHCancelBtnImage = UseLoadedButtonImage( giMPHStartBtnImage, -1,1,-1,3,-1 ); + guiMPHCancelButton = CreateIconAndTextButton( giMPHCancelBtnImage, gzMPHScreenText[MPH_CANCEL_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPH_CANCEL_X, MPH_BTN_START_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPHCancelCallback ); + SpecifyButtonSoundScheme( guiMPHCancelButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + + // Initialise Text Boxes + InitTextInputMode(); // API call to initialise text input mode for this screen + // does not mean we are inputting text right away + + // Player Name field + SetTextInputCursor( CUROSR_IBEAM_WHITE ); + SetTextInputFont( (UINT16) FONT12ARIALFIXEDWIDTH ); //FONT12ARIAL //FONT12ARIALFIXEDWIDTH + Set16BPPTextFieldColor( Get16BPPColor(FROMRGB( 0, 0, 0) ) ); + SetBevelColors( Get16BPPColor(FROMRGB(136, 138, 135)), Get16BPPColor(FROMRGB(24, 61, 81)) ); + SetTextInputRegularColors( FONT_WHITE, 2 ); + SetTextInputHilitedColors( 2, FONT_WHITE, FONT_WHITE ); + SetCursorColor( Get16BPPColor(FROMRGB(255, 255, 255) ) ); + + //AddUserInputField( NULL ); // API Call that sets a special input-handling routine and method for the TAB key + + //Add Player Name textbox + AddTextInputField( MPH_TXT_SVRNAME_X, + MPH_TXT_SVRNAME_Y, + MPH_TXT_SVRNAME_WIDTH, + MPH_TXT_SVRNAME_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzServerNameField, + 30, + INPUTTYPE_ASCII );//23 + + //Add Num Players textbox + AddTextInputField( MPH_TXT_MAXPLAYERS_X, + MPH_TXT_MAXPLAYERS_Y, + MPH_TXT_MAXPLAYERS_WIDTH, + MPH_TXT_MAXPLAYERS_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzMaxPlayersField, + 2, + INPUTTYPE_ASCII );//23 + + //Add Squad Size textbox + AddTextInputField( MPH_TXT_SQUAD_X, + MPH_TXT_SQUAD_Y, + MPH_TXT_SQUAD_WIDTH, + MPH_TXT_SQUAD_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzSquadSizeField, + 2, + INPUTTYPE_ASCII );//23 + + //Add Time of day textbox + AddTextInputField( MPH_TXT_TIME_X, + MPH_TXT_TIME_Y, + MPH_TXT_TIME_WIDTH, + MPH_TXT_TIME_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzTimeOfDayField, + 5, + INPUTTYPE_ASCII );//23 + + //Add Starting Cash textbox + AddTextInputField( MPH_TXT_CASH_X, + MPH_TXT_CASH_Y, + MPH_TXT_CASH_WIDTH, + MPH_TXT_CASH_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzStartingBalanceField, + 10, + INPUTTYPE_ASCII );//23 + + //Add Damage Multiplyer textbox + AddTextInputField( MPH_TXT_DMG_X, + MPH_TXT_DMG_Y, + MPH_TXT_DMG_WIDTH, + MPH_TXT_DMG_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzDmgMultiplierField, + 5, + INPUTTYPE_ASCII );//23 + + //Add Turn Timer textbox + AddTextInputField( MPH_TXT_TIMER_X, + MPH_TXT_TIMER_Y, + MPH_TXT_TIMER_WIDTH, + MPH_TXT_TIMER_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzTimerField, + 5, + INPUTTYPE_ASCII );//23 + + + SetActiveField( 0 ); // Playername textbox has focus + + + // + //Check box to toggle Difficulty settings + // + usPosY = MPH_DIF_SETTINGS_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y; + + for( cnt=0; cntuiFlags |= BUTTON_CLICKED_ON; + + else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) + ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_MED ] ]->uiFlags |= BUTTON_CLICKED_ON; + + else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) + ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_HARD ] ]->uiFlags |= BUTTON_CLICKED_ON; + + else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) + ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_INSANE ] ]->uiFlags |= BUTTON_CLICKED_ON; + + else + ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_MED ] ]->uiFlags |= BUTTON_CLICKED_ON; + + + + + // + //Check box to toggle GameType settings + // + usPosY = MPH_GAMETYPE_SETTINGS_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y; + + for( cnt=0; cntuiFlags |= BUTTON_CLICKED_ON; + + + // Random Mercs + guiMPHRandomMercsToggle = CreateCheckBoxButton( MPH_RNDMERC_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_RNDMERC_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHRandomMercCallback ); + if ( giMPHRandomMercs ) + ButtonList[ guiMPHRandomMercsToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + //checkbox for same merc + guiMPHSameMercToggle = CreateCheckBoxButton( MPH_SAMEMERC_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_SAMEMERC_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHSameMercCallback ); + if ( giMPHSameMercs ) + ButtonList[ guiMPHSameMercToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + //checkbox for report purchase + guiMPHReportMercToggle = CreateCheckBoxButton( MPH_REPORTMERC_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_REPORTMERC_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHReportMercCallback ); + if (giMPHReportMercs) + ButtonList[ guiMPHReportMercToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + //checkbox for bobby rays + guiMPHBobbyRayToggle = CreateCheckBoxButton( MPH_BOBBYRAY_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_BOBBYRAY_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHBobbyRayCallback ); + + if (!giMPHBobbyRays) + ButtonList[ guiMPHBobbyRayToggle ]->uiFlags |= BUTTON_CLICKED_ON; // the setting is actually DISABLE / NEGATIVE + + //checkbox for random spawn pos + guiMPHRandomSpawnToggle = CreateCheckBoxButton( MPH_RNDSPAWN_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_RNDSPAWN_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHRandomSpawnCallback ); + + if (giMPHRandomSpawn) + ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + // checkbox for civillians + guiMPHCivsToggle = CreateCheckBoxButton( MPH_CIVS_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_CIVS_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHCivsCallback ); + if (giMPHEnableCivilians) + ButtonList[ guiMPHCivsToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + // checkbox for use NIV + guiMPHUseNIVToggle = CreateCheckBoxButton( MPH_USENIV_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_USENIV_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHUseNIVCallback ); + if (giMPHUseNIV) + ButtonList[ guiMPHUseNIVToggle ]->uiFlags |= BUTTON_CLICKED_ON; + + + + + //Reset the exit screen - screen the main game loop will call next iteration + gubMPHExitScreen = MP_HOST_SCREEN; + + //REnder the screen once so we can blt ot to ths save buffer + RenderMPHScreen(); + + BlitBufferToBuffer(guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + + gfMPHButtonsAllocated = TRUE; + + return( TRUE ); + +} // End of EnterMPHScreen() + + +BOOLEAN ExitMPHScreen() +{ + UINT16 cnt; + + if( !gfMPHButtonsAllocated ) + return( TRUE ); + + //Delete the main options screen background + DeleteVideoObjectFromIndex( guiMPHMainBackGroundImage ); + + RemoveButton( guiMPHStartButton ); + RemoveButton( guiMPHCancelButton ); + + + UnloadButtonImage( giMPHCancelBtnImage ); + UnloadButtonImage( giMPHStartBtnImage ); + + //Check box to toggle Difficulty settings + for( cnt=0; cnt review this, i think MPH_EXIT is the proceed mode... + //if( gubMPHScreenHandler == MPH_EXIT ) + // SetMusicMode( MUSIC_NONE ); + + gfMPHScreenExit = FALSE; + gfMPHScreenEntry = TRUE; + + return( TRUE ); + +} // End of ExitMPHScreen() + + +void HandleMPHScreen() +{ + if( gubMPHScreenHandler != MPH_NOTHING ) + { + switch( gubMPHScreenHandler ) + { + case MPH_CANCEL: + gubMPHExitScreen = MAINMENU_SCREEN; + gfMPHScreenExit = TRUE; + break; + + + case MPH_START: + { + //if we are already fading out, get out of here + if( gFadeOutDoneCallback != DoneFadeOutForExitMPHScreen ) + { + //Disable the ok button + DisableButton( guiMPHStartButton ); + + gFadeOutDoneCallback = DoneFadeOutForExitMPHScreen; + + FadeOutNextFrame( ); + } + break; + } + + } + + gubMPHScreenHandler = MPH_NOTHING; + } + + + if( gfReRenderMPHScreen ) + { + RenderMPHScreen(); + gfReRenderMPHScreen = FALSE; + } + + RestoreMPHButtonBackGrounds(); +} // end of HandleMPHScreen + + +BOOLEAN RenderMPHScreen() +{ + HVOBJECT hPixHandle; + UINT16 usPosY; + + //Get the main background screen graphic and blt it + GetVideoObject(&hPixHandle, guiMPHMainBackGroundImage ); + BltVideoObject(FRAME_BUFFER, hPixHandle, 0,0,0, VO_BLT_SRCTRANSPARENCY,NULL); + + //Shade the background + ShadowVideoSurfaceRect( FRAME_BUFFER, iScreenWidthOffset, iScreenHeightOffset, iScreenWidthOffset + 640, iScreenHeightOffset + 480 ); + + //Display the title + DrawTextToScreen( gzMPHScreenText[ MPH_TITLE_TEXT ], MPH_MAIN_TITLE_X, MPH_MAIN_TITLE_Y, MPH_MAIN_TITLE_WIDTH, MPH_TITLE_FONT, MPH_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + + // Server name text label + DisplayWrappedString( MPH_LABEL_SVRNAME_X, MPH_LABEL_SVRNAME_Y, MPH_LABEL_SVRNAME_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_SERVERNAME_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Num Players text label + DisplayWrappedString( MPH_LABEL_MAXPLAYERS_X, MPH_LABEL_MAXPLAYERS_Y, MPH_LABEL_MAXPLAYERS_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_NUMPLAYERS_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Squad Size text label + DisplayWrappedString( MPH_LABEL_SQUAD_X, MPH_LABEL_SQUAD_Y, MPH_LABEL_SQUAD_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_SQUADSIZE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Time Of day text label + DisplayWrappedString( MPH_LABEL_TIME_X, MPH_LABEL_TIME_Y, MPH_LABEL_TIME_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_TIME_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Starting Cash text label + DisplayWrappedString( MPH_LABEL_CASH_X, MPH_LABEL_CASH_Y, MPH_LABEL_CASH_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_BALANCE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Starting Cash text label + DisplayWrappedString( MPH_LABEL_DMG_X, MPH_LABEL_DMG_Y, MPH_LABEL_DMG_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_DMG_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Starting Cash text label + DisplayWrappedString( MPH_LABEL_TIMER_X, MPH_LABEL_TIMER_Y, MPH_LABEL_TIMER_WIDTH, 2, MPH_LABEL_TEXT_FONT, MPH_LABEL_TEXT_COLOR, gzMPHScreenText[ MPH_TIMER_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + //Display the Dif Settings Title Text + DisplayWrappedString( MPH_DIF_SETTINGS_X, (UINT16)(MPH_DIF_SETTINGS_Y-MPH_GAP_BN_SETTINGS), MPH_DIF_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzGIOScreenText[ GIO_DIF_LEVEL_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Novice + usPosY = MPH_DIF_SETTINGS_Y+2; + DisplayWrappedString( (UINT16)(MPH_DIF_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_DIF_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzGIOScreenText[ GIO_EASY_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Intermediate + usPosY += MPH_GAP_BN_SETTINGS-5; + DisplayWrappedString( (UINT16)(MPH_DIF_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_DIF_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzGIOScreenText[ GIO_MEDIUM_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Hard + usPosY += MPH_GAP_BN_SETTINGS-5; + DisplayWrappedString( (UINT16)(MPH_DIF_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_DIF_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzGIOScreenText[ GIO_HARD_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Insane + usPosY += MPH_GAP_BN_SETTINGS-5; + //DrawTextToScreen( gzMPHScreenText[ MPH_HARD_TEXT ], (UINT16)(MPH_DIF_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_MAIN_TITLE_WIDTH, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + DisplayWrappedString( (UINT16)(MPH_DIF_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_DIF_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzGIOScreenText[ GIO_INSANE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + //Display the Game Type title + DisplayWrappedString( MPH_GAMETYPE_SETTINGS_X, (UINT16)(MPH_GAMETYPE_SETTINGS_Y-MPH_GAP_BN_SETTINGS), MPH_GAMETYPE_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_GAMETYPE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Deathmatch + usPosY = MPH_GAMETYPE_SETTINGS_Y+2; + DisplayWrappedString( (UINT16)(MPH_GAMETYPE_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_GAMETYPE_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_DEATHMATCH_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Team Deathmatch + usPosY += MPH_GAP_BN_SETTINGS-5; + DisplayWrappedString( (UINT16)(MPH_GAMETYPE_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_GAMETYPE_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_TEAMDM_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Co-operative + usPosY += MPH_GAP_BN_SETTINGS-5; + DisplayWrappedString( (UINT16)(MPH_GAMETYPE_SETTINGS_X+MPH_OFFSET_TO_TEXT), usPosY, MPH_GAMETYPE_SETTINGS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_COOP_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Random Mercs + DisplayWrappedString( (UINT16)MPH_RNDMERC_X, MPH_RNDMERC_Y, MPH_RNDMERC_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_RANDOMMERCS_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Same Mercs + DisplayWrappedString( (UINT16)MPH_SAMEMERC_X, MPH_SAMEMERC_Y, MPH_SAMEMERC_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_SAMEMERC_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Report Mercs + DisplayWrappedString( (UINT16)MPH_REPORTMERC_X, MPH_REPORTMERC_Y, MPH_REPORTMERC_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_RPTMERC_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Allow Bobby-Rays + DisplayWrappedString( (UINT16)MPH_BOBBYRAY_X, MPH_BOBBYRAY_Y, MPH_BOBBYRAY_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_BOBBYRAY_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Random Spawn + DisplayWrappedString( (UINT16)MPH_RNDSPAWN_X, MPH_RNDSPAWN_Y, MPH_RNDSPAWN_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_RNDMSTART_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Civs + DisplayWrappedString( (UINT16)MPH_CIVS_X, MPH_CIVS_Y, MPH_CIVS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_ENABLECIV_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Use NIV + DisplayWrappedString( (UINT16)MPH_USENIV_X, MPH_USENIV_Y, MPH_CIVS_WIDTH, 2, MPH_TOGGLE_TEXT_FONT, MPH_TOGGLE_TEXT_COLOR, gzMPHScreenText[ MPH_USENIV_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + + return( TRUE ); +} // end of RenderMPHScreen() + + + +void GetMPHScreenUserInput() +{ + InputAtom Event; +// POINT MousePos; + +// GetCursorPos(&MousePos); + + while( DequeueEvent( &Event ) ) + { + // check if this event is swallowed by text input, otherwise process key + if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN ) + { + switch( Event.usParam ) + { + + case ESC: + //Exit out of the screen + gubMPHScreenHandler = MPH_CANCEL; + break; + + case ENTER: + if (ValidateMPSettings()) + { + SaveMPSettings(); + gubMPHScreenHandler = MPH_START; + } + break; + } + } + } +} // end of GetMPHScreenUserInput() + + +// CALLBACKS + +void BtnMPHStartCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + if (ValidateMPSettings()) + { + SaveMPSettings(); + gubMPHScreenHandler = MPH_START; + } + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +void BtnMPHCancelCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + gubMPHScreenHandler = MPH_CANCEL; + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +void BtnMPHDifficultyTogglesCallback( GUI_BUTTON *btn, INT32 reason ) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + /*UINT8 ubButton = (UINT8)*/MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + UINT8 cnt; + + for( cnt=0; cntuiFlags &= ~BUTTON_CLICKED_ON; + } + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + else + { + UINT8 cnt; + BOOLEAN fAnyChecked=FALSE; + + //if none of the other boxes are checked, do not uncheck this box + for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) + { + fAnyChecked = TRUE; + } + } + //if none are checked, re check this one + if( !fAnyChecked ) + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHGameTypeTogglesCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + /*UINT8 ubButton = (UINT8)*/MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + UINT8 cnt; + + for( cnt=0; cntuiFlags &= ~BUTTON_CLICKED_ON; + } + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + else + { + UINT8 cnt; + BOOLEAN fAnyChecked=FALSE; + + //if none of the other boxes are checked, do not uncheck this box + for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) + { + fAnyChecked = TRUE; + } + } + //if none are checked, re check this one + if( !fAnyChecked ) + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void RestoreMPHButtonBackGrounds() +{ + UINT8 cnt; + UINT16 usPosY; + + + usPosY = MPH_DIF_SETTINGS_Y-MPH_OFFSET_TO_TOGGLE_BOX_Y; + //Check box to toggle Difficulty settings + for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) + { + return( cnt ); + } + } + + return( 0 ); +} + +UINT8 GetMPHGameTypeButtonSetting() +{ + UINT8 cnt; + + for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) + { + return( cnt ); + } + } + + return( 0 ); +} + +BOOLEAN DoMPHMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ) +{ + SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; + + // reset exit mode +// gfExitGioDueToMessageBox = TRUE; + + // do message box and return + giMPHMessageBox = DoMessageBox( ubStyle, zString, uiExitScreen, ( UINT16 ) ( usFlags| MSG_BOX_FLAG_USE_CENTERING_RECT ), ReturnCallback, &CenteringRect ); + + // send back return state + return( ( giMPHMessageBox != -1 ) ); +} + +void DoneFadeOutForExitMPHScreen( void ) +{ + // As we bypassed the GIO screen, set up some game options for multiplayer here + // most things i have left as thier defaults here for testing. + is_networked = true; + is_host = true; // we want to be the host, not we ARE the host yet (is_server) + auto_retry = true; + giNumTries = 5; + + // loop through and get the status of all the buttons + // Madd + /*gGameOptions.fGunNut = GetCurrentGunButtonSetting(); + gGameOptions.ubGameStyle = GetCurrentGameStyleButtonSetting(); + gGameOptions.ubDifficultyLevel = GetCurrentDifficultyButtonSetting() + 1;*/ + // JA2Gold: no more timed turns setting + //gGameOptions.fTurnTimeLimit = GetCurrentTimedTurnsButtonSetting();//hayden : re-enabled + + if (is_networked) + gGameOptions.fTurnTimeLimit = TRUE; + else + gGameOptions.fTurnTimeLimit = FALSE; + + // JA2Gold: iron man + //gGameOptions.fIronManMode = GetCurrentGameSaveButtonSetting(); + + // Bobby Rays - why would we want anything less than the best + gGameOptions.ubBobbyRay = BR_AWESOME; + + + // CHRISL: + /*if(IsNIVModeValid() == TRUE){ + switch ( GetCurrentINVOptionButtonSetting() ) + { + case GIO_INV_OLD: + gGameOptions.ubInventorySystem = INVENTORY_OLD; + break; + case GIO_INV_NEW: + gGameOptions.ubInventorySystem = INVENTORY_NEW; + break; + } + }*/ + + // gubGIOExitScreen = INIT_SCREEN; + gubMPHExitScreen = INTRO_SCREEN; + + //set the fact that we should do the intro videos +// gbIntroScreenMode = INTRO_BEGINING; +#ifdef JA2TESTVERSION + if( gfKeyState[ ALT ] ) + { + if( gfKeyState[ CTRL ] ) + { + gMercProfiles[ MIGUEL ].bMercStatus = MERC_IS_DEAD; + gMercProfiles[ SKYRIDER ].bMercStatus = MERC_IS_DEAD; + } + + SetIntroType( INTRO_ENDING ); + } + else +#endif + SetIntroType( INTRO_BEGINING ); + + ExitMPHScreen(); // cleanup please, if we called a fadeout then we didnt do it above + + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void DoneFadeInForExitMPHScreen( void ) +{ + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + + +void BtnMPHRandomMercCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHRandomMercsToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHSameMercCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHSameMercToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHReportMercCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHReportMercToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHBobbyRayCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHBobbyRayToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHRandomSpawnCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHUseNIVCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHUseNIVToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + +void BtnMPHCivsCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHCivsToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} \ No newline at end of file diff --git a/MPHostScreen.h b/MPHostScreen.h new file mode 100644 index 00000000..051947ff --- /dev/null +++ b/MPHostScreen.h @@ -0,0 +1,12 @@ +#ifndef _MP_HOST_SCREEN_H_ +#define _MP_HOST_SCREEN_H_ + + +UINT32 MPHostScreenInit( void ); +UINT32 MPHostScreenHandle( void ); +UINT32 MPHostScreenShutdown( void ); + + + + +#endif \ No newline at end of file diff --git a/MPJoinScreen.cpp b/MPJoinScreen.cpp new file mode 100644 index 00000000..88fdb9bb --- /dev/null +++ b/MPJoinScreen.cpp @@ -0,0 +1,742 @@ +#ifdef PRECOMPILEDHEADERS + #include "JA2 All.h" + #include "Intro.h" +#else + #include "Types.h" + #include "MPJoinScreen.h" + #include "GameSettings.h" + #include "Utilities.h" + #include "wCheck.h" + #include "Font Control.h" + #include "WordWrap.h" + #include "Render Dirty.h" + #include "Input.h" + #include "Options Screen.h" + #include "English.h" + #include "Sysutil.h" + #include "Fade Screen.h" + #include "Cursor Control.h" + #include "Music Control.h" + #include "cursors.h" + #include "Intro.h" + #include "Text.h" + #include "Text Input.h" + #include "_Ja25EnglishText.h" + #include "Soldier Profile.h" +#endif + +#include "gameloop.h" +#include "connect.h" +#include "network.h" // for client name +#include "saveloadscreen.h" + + +//////////////////////////////////////////// +// +// Global Defines +// +/////////////////////////////////////////// + +#define MPJ_TITLE_FONT FONT14ARIAL//FONT16ARIAL +#define MPJ_TITLE_COLOR FONT_MCOLOR_WHITE + +#define MPJ_LABEL_TEXT_FONT FONT12ARIAL//FONT16ARIAL +#define MPJ_LABEL_TEXT_COLOR FONT_MCOLOR_WHITE + +//buttons +#define MPJ_BTN_JOIN_X iScreenWidthOffset + 320 + 143 +#define MPJ_BTN_JOIN_Y iScreenHeightOffset + 435 +#define MPJ_BTN_HOST_X MPJ_BTN_JOIN_X-180 +#define MPJ_BTN_HOST_Y iScreenHeightOffset + 435 +#define MPJ_CANCEL_X iScreenWidthOffset + ((320 - 115) / 2) + +//textboxes +#define MPJ_TXT_HANDLE_X iScreenWidthOffset + 100 +#define MPJ_TXT_HANDLE_Y iScreenHeightOffset + 60 +#define MPJ_TXT_HANDLE_WIDTH 120 +#define MPJ_TXT_HANDLE_HEIGHT 17 +#define MPJ_TXT_IP_X iScreenWidthOffset + 100 +#define MPJ_TXT_IP_Y iScreenHeightOffset + 400 +#define MPJ_TXT_IP_WIDTH 100 +#define MPJ_TXT_IP_HEIGHT 17 +#define MPJ_TXT_PORT_X MPJ_TXT_IP_X + MPJ_TXT_IP_WIDTH + 40 +#define MPJ_TXT_PORT_Y iScreenHeightOffset + 400 +#define MPJ_TXT_PORT_WIDTH 40 +#define MPJ_TXT_PORT_HEIGHT 17 + + +//main title +#define MPJ_MAIN_TITLE_X 0 +#define MPJ_MAIN_TITLE_Y iScreenHeightOffset + 10 +#define MPJ_MAIN_TITLE_WIDTH SCREEN_WIDTH + +//labels +#define MPJ_LABEL_HANDLE_X MPJ_TXT_HANDLE_X - 80 +#define MPJ_LABEL_HANDLE_Y MPJ_TXT_HANDLE_Y + 3 +#define MPJ_LABEL_HANDLE_WIDTH 80 +#define MPJ_LABEL_HANDLE_HEIGHT 17 +#define MPJ_LABEL_IP_X MPJ_TXT_IP_X - 80 +#define MPJ_LABEL_IP_Y MPJ_TXT_IP_Y + 3 +#define MPJ_LABEL_IP_WIDTH 80 +#define MPJ_LABEL_IP_HEIGHT 17 +#define MPJ_LABEL_PORT_X MPJ_TXT_PORT_X - 30 +#define MPJ_LABEL_PORT_Y MPJ_TXT_PORT_Y + 3 +#define MPJ_LABEL_PORT_WIDTH 80 +#define MPJ_LABEL_PORT_HEIGHT 17 + + +//////////////////////////////////////////// +// +// Global Variables +// +/////////////////////////////////////////// + +BOOLEAN gfMPJScreenEntry = TRUE; +BOOLEAN gfMPJScreenExit = FALSE; +BOOLEAN gfReRenderMPJScreen=TRUE; +BOOLEAN gfMPJButtonsAllocated = FALSE; + +//enum for different states of screen +enum +{ + MPJ_NOTHING, + MPJ_CANCEL, + MPJ_EXIT, + MPJ_HOST, + MPJ_JOIN +}; + +UINT8 gubMPJScreenHandler=MPJ_NOTHING; // State changer for HandleMPJScreen() + +UINT32 gubMPJExitScreen = MP_JOIN_SCREEN; // The screen that is in control next iteration of the game_loop + +UINT32 guiMPJMainBackGroundImage; + +// Wide-char strings that will hold the variables until they are transferred to the CHAR ascii fields +CHAR16 gzPlayerHandleField[ 30 ] = {0} ; +CHAR16 gzServerIPField[ 15 ] = {0} ; +CHAR16 gzServerPortField[ 5 ] = {0} ; + +//////////////////////////////////////////// +// +// Screen Controls +// +/////////////////////////////////////////// + +// Join Button +void BtnMPJoinCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPJoinButton; +INT32 giMPJoinBtnImage; + +// Host Button +void BtnMPJHostCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPJHostButton; +INT32 giMPJHostBtnImage; + +// Cancel Button +void BtnMPJCancelCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPJCancelButton; +INT32 giMPJCancelBtnImage; + +// Message Box handle +INT8 giMPJMessageBox = -1; + + +//////////////////////////////////////////// +// +// Local Function Prototypes +// +/////////////////////////////////////////// + +extern void ClearMainMenu(); + +BOOLEAN EnterMPJScreen(); +BOOLEAN ExitMPJScreen(); +void HandleMPJScreen(); +BOOLEAN RenderMPJScreen(); +void GetMPJScreenUserInput(); +void SaveJoinSettings(); +bool ValidateJoinSettings(bool bSkipServerAddress); +BOOLEAN DoMPJMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ); +void DoneFadeOutForExitMPJScreen( void ); +void DoneFadeInForExitMPJScreen( void ); + + +UINT32 MPJoinScreenInit( void ) +{ + // read settings from JA2 ini + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_IP", L"", gzServerIPField, 15, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_PORT", L"", gzServerPortField, 5, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"CLIENT_NAME", L"Fresh Meat", gzPlayerHandleField, 30 , L"..\\Ja2_mp.ini" ); + return( 1 ); +} + +void SaveJoinSettings() +{ + Get16BitStringFromField( 0, gzPlayerHandleField ); // these indexes are based on the order created + Get16BitStringFromField( 1, gzServerIPField ); + Get16BitStringFromField( 2, gzServerPortField ); + + // save settings to JA2_mp.ini + WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_IP", gzServerIPField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_PORT", gzServerPortField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"CLIENT_NAME", gzPlayerHandleField , L"..\\Ja2_mp.ini" ); +} + +bool ValidateJoinSettings(bool bSkipServerAddress) +{ + // Check a Server name is entered + Get16BitStringFromField( 0, gzPlayerHandleField ); // these indexes are based on the order created + if (wcscmp(gzPlayerHandleField,L"")<=0) + { + DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_HANDLE_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // dont check server address if we are going to HOST + if (bSkipServerAddress) + return true; + + // Verify the IP Address + Get16BitStringFromField( 1, gzServerIPField ); + + // loop through octets and check + int numOctets = 0; + wchar_t* tok; + tok = wcstok(gzServerIPField,L"."); + while (tok != NULL) + { + numOctets++; + INT32 oct = _wtoi(tok); + // check for invalid conversion, ie alpha chars + // wtoi returns 0 if it cant convert, but we need this value + // therefore if tok <> 0 then it was a bad convert. + if (oct == 0 && wcscmp(tok,L"0") != 0) + { + // force error + numOctets=0; + break; + } + + if (oct < 0 || oct > 254) // dont allow broadcast nums + { + // bad octet, error + numOctets=0; + break; + } + + // get next octet + tok = wcstok(NULL,L"."); + } + + if (numOctets != 4) + { + // not a valid ip address + DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERIP_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // Verify the Server Port + Get16BitStringFromField( 2, gzServerPortField ); + INT32 svrPort = _wtoi(gzServerPortField); + if (svrPort < 1 || svrPort > 65535) + { + DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERPORT_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + return true; +} + +UINT32 MPJoinScreenHandle( void ) +{ + StartFrameBufferRender(); + + if( gfMPJScreenEntry ) + { +// PauseGame(); + + EnterMPJScreen(); + gfMPJScreenEntry = FALSE; + gfMPJScreenExit = FALSE; + InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + } + + GetMPJScreenUserInput(); + + + HandleMPJScreen(); + + // render buttons marked dirty + MarkButtonsDirty( ); + RenderButtons( ); + + // render text boxes + RenderAllTextFields(); // textbox system call + + // render help +// RenderFastHelp( ); +// RenderButtonsFastHelp( ); + + + ExecuteBaseDirtyRectQueue(); + EndFrameBufferRender(); + + // handle fades in and out + if ( HandleFadeOutCallback( ) ) + { + ClearMainMenu(); + return( gubMPJExitScreen ); + } + + if ( HandleBeginFadeOut( gubMPJExitScreen ) ) + { + return( gubMPJExitScreen ); + } + + if ( HandleFadeInCallback( ) ) + { + // Re-render the scene! + RenderMPJScreen(); + } + + if ( HandleBeginFadeIn( gubMPJExitScreen ) ) + { + + } + + if( gfMPJScreenExit ) // we are exiting this screen + { + ExitMPJScreen(); // perform destruction + } + + return( gubMPJExitScreen ); +} // end MPJoinScreenHandle() + + +UINT32 MPJoinScreenShutdown( void ) +{ + return( 1 ); +} + +BOOLEAN EnterMPJScreen() +{ + VOBJECT_DESC VObjectDesc; + UINT16 cnt; + UINT16 usPosY; + + if( gfMPJButtonsAllocated ) + return( TRUE ); + + SetCurrentCursorFromDatabase( CURSOR_NORMAL ); + + // load the Main trade screen backgroiund image + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_800x600.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_1024x768.sti", VObjectDesc.ImageFile); + } + + CHECKF(AddVideoObject(&VObjectDesc, &guiMPJMainBackGroundImage )); + + //Join button + giMPJoinBtnImage = LoadButtonImage("INTERFACE\\PreferencesButtons.sti", -1,0,-1,2,-1 ); + guiMPJoinButton = CreateIconAndTextButton( giMPJoinBtnImage, gzMPJScreenText[MPJ_JOIN_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPJ_BTN_JOIN_X, MPJ_BTN_JOIN_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPJoinCallback); + + SpecifyButtonSoundScheme( guiMPJoinButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPJoinButton, DISABLED_STYLE_NONE ); + + //Host button + giMPJHostBtnImage = UseLoadedButtonImage( giMPJoinBtnImage, -1,1,-1,3,-1 ); + guiMPJHostButton = CreateIconAndTextButton( giMPJHostBtnImage, gzMPJScreenText[MPJ_HOST_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPJ_BTN_HOST_X, MPJ_BTN_HOST_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPJHostCallback); + + SpecifyButtonSoundScheme( guiMPJHostButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPJHostButton, DISABLED_STYLE_NONE ); + + //Cancel button + giMPJCancelBtnImage = UseLoadedButtonImage( giMPJoinBtnImage, -1,1,-1,3,-1 ); + guiMPJCancelButton = CreateIconAndTextButton( giMPJCancelBtnImage, gzMPJScreenText[MPJ_CANCEL_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPJ_CANCEL_X, MPJ_BTN_JOIN_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPJCancelCallback ); + SpecifyButtonSoundScheme( guiMPJCancelButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + + // Initialise Text Boxes + InitTextInputMode(); // API call to initialise text input mode for this screen + // does not mean we are inputting text right away + + // Player Name field + SetTextInputCursor( CUROSR_IBEAM_WHITE ); + SetTextInputFont( (UINT16) FONT12ARIALFIXEDWIDTH ); //FONT12ARIAL //FONT12ARIALFIXEDWIDTH + Set16BPPTextFieldColor( Get16BPPColor(FROMRGB( 0, 0, 0) ) ); + SetBevelColors( Get16BPPColor(FROMRGB(136, 138, 135)), Get16BPPColor(FROMRGB(24, 61, 81)) ); + SetTextInputRegularColors( FONT_WHITE, 2 ); + SetTextInputHilitedColors( 2, FONT_WHITE, FONT_WHITE ); + SetCursorColor( Get16BPPColor(FROMRGB(255, 255, 255) ) ); + + //AddUserInputField( NULL ); // API Call that sets a special input-handling routine and method for the TAB key + + //Add Player Name textbox + AddTextInputField( MPJ_TXT_HANDLE_X, + MPJ_TXT_HANDLE_Y, + MPJ_TXT_HANDLE_WIDTH, + MPJ_TXT_HANDLE_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzPlayerHandleField, + 30, + INPUTTYPE_ASCII );//23 + + //Add Server IP textbox + AddTextInputField( MPJ_TXT_IP_X, + MPJ_TXT_IP_Y, + MPJ_TXT_IP_WIDTH, + MPJ_TXT_IP_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzServerIPField, + 15, + INPUTTYPE_ASCII );//23 + + //Add Server Port textbox + AddTextInputField( MPJ_TXT_PORT_X, + MPJ_TXT_PORT_Y, + MPJ_TXT_PORT_WIDTH, + MPJ_TXT_PORT_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzServerPortField, + 5, + INPUTTYPE_ASCII );//23 + + SetActiveField( 0 ); // Playername textbox has focus + + //Reset the exit screen - screen the main game loop will call next iteration + gubMPJExitScreen = MP_JOIN_SCREEN; + + //REnder the screen once so we can blt ot to ths save buffer + RenderMPJScreen(); + + BlitBufferToBuffer(guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + + gfMPJButtonsAllocated = TRUE; + + return( TRUE ); + +} // End of EnterMPJScreen() + + +BOOLEAN ExitMPJScreen() +{ + UINT16 cnt; + + if( !gfMPJButtonsAllocated ) + return( TRUE ); + + //Delete the main options screen background + DeleteVideoObjectFromIndex( guiMPJMainBackGroundImage ); + + RemoveButton( guiMPJoinButton ); + RemoveButton( guiMPJHostButton ); + RemoveButton( guiMPJCancelButton ); + + + UnloadButtonImage( giMPJCancelBtnImage ); + UnloadButtonImage( giMPJHostBtnImage ); + UnloadButtonImage( giMPJoinBtnImage ); + + // exit text input mode in this screen and clean up text boxes + KillAllTextInputModes(); + SetTextInputCursor( CURSOR_IBEAM ); + + gfMPJButtonsAllocated = FALSE; + + //If we are starting the game stop playing the music + // review this, i think MPJ_EXIT is the proceed mode... + //if( gubMPJScreenHandler == MPJ_EXIT ) + // SetMusicMode( MUSIC_NONE ); + + gfMPJScreenExit = FALSE; + gfMPJScreenEntry = TRUE; + + return( TRUE ); + +} // End of ExitMPJScreen() + + +void HandleMPJScreen() +{ + if( gubMPJScreenHandler != MPJ_NOTHING ) + { + switch( gubMPJScreenHandler ) + { + case MPJ_CANCEL: + gubMPJExitScreen = MAINMENU_SCREEN; + gfMPJScreenExit = TRUE; + break; + + case MPJ_HOST: + gubMPJExitScreen = MP_HOST_SCREEN; + gfMPJScreenExit = TRUE; + break; + + + case MPJ_JOIN: + { + //if we are already fading out, get out of here + if( gFadeOutDoneCallback != DoneFadeOutForExitMPJScreen ) + { + //Disable the ok button + DisableButton( guiMPJoinButton ); + DisableButton( guiMPJHostButton ); + + gFadeOutDoneCallback = DoneFadeOutForExitMPJScreen; + + FadeOutNextFrame( ); + } + break; + } + + } + + gubMPJScreenHandler = MPJ_NOTHING; + } + + + if( gfReRenderMPJScreen ) + { + RenderMPJScreen(); + gfReRenderMPJScreen = FALSE; + } + + // add restore backgrounds in... + //RestoreGIOButtonBackGrounds(); +} // end of HandleMPJScreen + + +BOOLEAN RenderMPJScreen() +{ + HVOBJECT hPixHandle; + UINT16 usPosY; + + //Get the main background screen graphic and blt it + GetVideoObject(&hPixHandle, guiMPJMainBackGroundImage ); + BltVideoObject(FRAME_BUFFER, hPixHandle, 0,0,0, VO_BLT_SRCTRANSPARENCY,NULL); + + //Shade the background + ShadowVideoSurfaceRect( FRAME_BUFFER, iScreenWidthOffset, iScreenHeightOffset, iScreenWidthOffset + 640, iScreenHeightOffset + 480 ); + + //Display the title + DrawTextToScreen( gzMPJScreenText[ MPJ_TITLE_TEXT ], MPJ_MAIN_TITLE_X, MPJ_MAIN_TITLE_Y, MPJ_MAIN_TITLE_WIDTH, MPJ_TITLE_FONT, MPJ_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + + // Player name text label + DisplayWrappedString( MPJ_LABEL_HANDLE_X, MPJ_LABEL_HANDLE_Y, MPJ_LABEL_HANDLE_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_HANDLE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Server IP text label + DisplayWrappedString( MPJ_LABEL_IP_X, MPJ_LABEL_IP_Y, MPJ_LABEL_IP_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_SERVERIP_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Server Port text label + DisplayWrappedString( MPJ_LABEL_PORT_X, MPJ_LABEL_PORT_Y, MPJ_LABEL_PORT_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_SERVERPORT_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + return( TRUE ); +} // end of RenderMPJScreen() + + + +void GetMPJScreenUserInput() +{ + InputAtom Event; +// POINT MousePos; + +// GetCursorPos(&MousePos); + + while( DequeueEvent( &Event ) ) + { + // check if this event is swallowed by text input, otherwise process key + if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN ) + { + switch( Event.usParam ) + { + + case ESC: + //Exit out of the screen + gubMPJScreenHandler = MPJ_CANCEL; + break; + + case ENTER: + if (ValidateJoinSettings(false)) + { + SaveJoinSettings(); + gubMPJScreenHandler = MPJ_JOIN; + } + break; + } + } + } +} // end of GetMPJScreenUserInput() + + +// CALLBACKS + +void BtnMPJoinCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + if (ValidateJoinSettings(false)) + { + SaveJoinSettings(); + gubMPJScreenHandler = MPJ_JOIN; + //gubMPJScreenHandler = MPJ_JOIN; + } + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +void BtnMPJHostCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + if (ValidateJoinSettings(true)) + { + SaveJoinSettings(); + gubMPJScreenHandler = MPJ_HOST; + } + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +void BtnMPJCancelCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + gubMPJScreenHandler = MPJ_CANCEL; + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} + +BOOLEAN DoMPJMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ) +{ + SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; + + // reset exit mode +// gfExitGioDueToMessageBox = TRUE; + + // do message box and return + giMPJMessageBox = DoMessageBox( ubStyle, zString, uiExitScreen, ( UINT16 ) ( usFlags| MSG_BOX_FLAG_USE_CENTERING_RECT ), ReturnCallback, &CenteringRect ); + + // send back return state + return( ( giMPJMessageBox != -1 ) ); +} + +void DoneFadeOutForExitMPJScreen( void ) +{ + // As we bypassed the GIO screen, set up some game options for multiplayer here + // most things i have left as thier defaults here for testing. + is_networked = true; + is_host = false; // we want to be a client, not we ARE a client yet (is_client) + auto_retry = true; + giNumTries = 5; + + // loop through and get the status of all the buttons + // Madd + /*gGameOptions.fGunNut = GetCurrentGunButtonSetting(); + gGameOptions.ubGameStyle = GetCurrentGameStyleButtonSetting(); + gGameOptions.ubDifficultyLevel = GetCurrentDifficultyButtonSetting() + 1;*/ + // JA2Gold: no more timed turns setting + //gGameOptions.fTurnTimeLimit = GetCurrentTimedTurnsButtonSetting();//hayden : re-enabled + + if (is_networked) + gGameOptions.fTurnTimeLimit = TRUE; + else + gGameOptions.fTurnTimeLimit = FALSE; + + // JA2Gold: iron man + //gGameOptions.fIronManMode = GetCurrentGameSaveButtonSetting(); + + // Bobby Rays - why would we want anything less than the best + gGameOptions.ubBobbyRay = BR_AWESOME; + + + // CHRISL: + /*if(IsNIVModeValid() == TRUE){ + switch ( GetCurrentINVOptionButtonSetting() ) + { + case GIO_INV_OLD: + gGameOptions.ubInventorySystem = INVENTORY_OLD; + break; + case GIO_INV_NEW: + gGameOptions.ubInventorySystem = INVENTORY_NEW; + break; + } + }*/ + + // gubGIOExitScreen = INIT_SCREEN; + gubMPJExitScreen = INTRO_SCREEN; + + //set the fact that we should do the intro videos +// gbIntroScreenMode = INTRO_BEGINING; +#ifdef JA2TESTVERSION + if( gfKeyState[ ALT ] ) + { + if( gfKeyState[ CTRL ] ) + { + gMercProfiles[ MIGUEL ].bMercStatus = MERC_IS_DEAD; + gMercProfiles[ SKYRIDER ].bMercStatus = MERC_IS_DEAD; + } + + SetIntroType( INTRO_ENDING ); + } + else +#endif + SetIntroType( INTRO_BEGINING ); + + ExitMPJScreen(); // cleanup please, if we called a fadeout then we didnt do it above + + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void DoneFadeInForExitMPJScreen( void ) +{ + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} \ No newline at end of file diff --git a/MPJoinScreen.h b/MPJoinScreen.h new file mode 100644 index 00000000..b03b6c61 --- /dev/null +++ b/MPJoinScreen.h @@ -0,0 +1,12 @@ +#ifndef _MP_JOIN_SCREEN_H_ +#define _MP_JOIN_SCREEN_H_ + + +UINT32 MPJoinScreenInit( void ); +UINT32 MPJoinScreenHandle( void ); +UINT32 MPJoinScreenShutdown( void ); + + + + +#endif \ No newline at end of file diff --git a/MPScoreScreen.cpp b/MPScoreScreen.cpp new file mode 100644 index 00000000..77000922 --- /dev/null +++ b/MPScoreScreen.cpp @@ -0,0 +1,642 @@ +#ifdef PRECOMPILEDHEADERS + #include "JA2 All.h" + #include "Intro.h" +#else + #include "Types.h" + #include "MPScoreScreen.h" + #include "GameSettings.h" + #include "Utilities.h" + #include "wCheck.h" + #include "Font Control.h" + #include "WordWrap.h" + #include "Render Dirty.h" + #include "Input.h" + #include "Options Screen.h" + #include "English.h" + #include "Sysutil.h" + #include "Fade Screen.h" + #include "Cursor Control.h" + #include "Music Control.h" + #include "cursors.h" + #include "Intro.h" + #include "Text.h" + #include "Text Input.h" + #include "_Ja25EnglishText.h" + #include "Soldier Profile.h" +#endif + +#include "gameloop.h" +#include "Game Init.h" +#include "connect.h" +#include "network.h" // for client name +#include "saveloadscreen.h" + + +//////////////////////////////////////////// +// +// Global Defines +// +/////////////////////////////////////////// + +#define MPS_TITLE_FONT FONT14ARIAL//FONT16ARIAL +#define MPS_TITLE_COLOR FONT_MCOLOR_WHITE + +#define MPS_LABEL_TEXT_FONT FONT12ARIAL//FONT16ARIAL +#define MPS_LABEL_TEXT_COLOR FONT_MCOLOR_WHITE + +//buttons +#define MPS_BTN_CONTINUE_X iScreenWidthOffset + 320 + 143 +#define MPS_BTN_CONTINUE_Y iScreenHeightOffset + 435 +//#define MPJ_BTN_HOST_X MPJ_BTN_JOIN_X-180 +//#define MPJ_BTN_HOST_Y iScreenHeightOffset + 435 +#define MPS_BTN_CANCEL_X iScreenWidthOffset + ((320 - 115) / 2) +#define MPS_BTN_CANCEL_Y MPS_BTN_CONTINUE_Y + +//main title +#define MPS_MAIN_TITLE_X 0 +#define MPS_MAIN_TITLE_Y iScreenHeightOffset + 10 +#define MPS_MAIN_TITLE_WIDTH SCREEN_WIDTH + +//labels +#define MPS_LABEL_PLAYER_X iScreenWidthOffset + 80 +#define MPS_LABEL_PLAYER_Y iScreenHeightOffset + 80 +#define MPS_LABEL_PLAYER_WIDTH 80 +#define MPS_LABEL_KILLS_X MPS_LABEL_PLAYER_X + MPS_LABEL_PLAYER_WIDTH + 20 +#define MPS_LABEL_KILLS_Y MPS_LABEL_PLAYER_Y +#define MPS_LABEL_KILLS_WIDTH 50 +#define MPS_LABEL_DEATHS_X MPS_LABEL_KILLS_X + MPS_LABEL_KILLS_WIDTH + 20 +#define MPS_LABEL_DEATHS_Y MPS_LABEL_PLAYER_Y +#define MPS_LABEL_DEATHS_WIDTH 50 +#define MPS_LABEL_HITS_X MPS_LABEL_DEATHS_X + MPS_LABEL_DEATHS_WIDTH + 20 +#define MPS_LABEL_HITS_Y MPS_LABEL_PLAYER_Y +#define MPS_LABEL_HITS_WIDTH 50 +#define MPS_LABEL_MISSES_X MPS_LABEL_HITS_X + MPS_LABEL_HITS_WIDTH + 20 +#define MPS_LABEL_MISSES_Y MPS_LABEL_PLAYER_Y +#define MPS_LABEL_MISSES_WIDTH 50 +#define MPS_LABEL_ACCURACY_X MPS_LABEL_MISSES_X + MPS_LABEL_MISSES_WIDTH + 20 +#define MPS_LABEL_ACCURACY_Y MPS_LABEL_PLAYER_Y +#define MPS_LABEL_ACCURACY_WIDTH 70 + + +#define MPS_PLAYERLIST_Y MPS_LABEL_PLAYER_Y + 30 +#define MPS_PLAYER_GAP 17 + + + +//////////////////////////////////////////// +// +// Global Variables +// +/////////////////////////////////////////// + +BOOLEAN gfMPSScreenEntry = TRUE; +BOOLEAN gfMPSScreenExit = FALSE; +BOOLEAN gfReRenderMPSScreen=TRUE; +BOOLEAN gfMPSButtonsAllocated = FALSE; + +//enum for different states of screen +enum +{ + MPS_NOTHING, + MPS_CANCEL, + MPS_CONTINUE, +}; + +UINT8 gubMPSScreenHandler=MPS_NOTHING; // State changer for HandleMPJScreen() + +UINT32 gubMPSExitScreen = MP_SCORE_SCREEN; // The screen that is in control next iteration of the game_loop + +UINT32 guiMPSMainBackGroundImage; + +//////////////////////////////////////////// +// +// Screen Controls +// +/////////////////////////////////////////// + +// Cancel Button +void BtnMPSCancelCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPSCancelButton; +INT32 giMPSCancelBtnImage; + +// Continue Button +void BtnMPSContinueCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPSContinueButton; +INT32 giMPSContinueBtnImage; + +// Message Box handle +INT8 giMPSMessageBox = -1; + + +//////////////////////////////////////////// +// +// Local Function Prototypes +// +/////////////////////////////////////////// + +extern void ClearMainMenu(); + +BOOLEAN EnterMPSScreen(); +BOOLEAN ExitMPSScreen(); +void HandleMPSScreen(); +BOOLEAN RenderMPSScreen(); + +void GetMPSScreenUserInput(); +void DoneFadeOutForExitMPSScreen( void ); +void DoneFadeInForExitMPSScreen( void ); + +UINT32 MPScoreScreenInit( void ) +{ + return (1); +} + +UINT32 MPScoreScreenHandle( void ) +{ + StartFrameBufferRender(); + + if( gfMPSScreenEntry ) + { +// PauseGame(); + + EnterMPSScreen(); + gfMPSScreenEntry = FALSE; + gfMPSScreenExit = FALSE; + InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + } + + GetMPSScreenUserInput(); + + + HandleMPSScreen(); + + // render buttons marked dirty + MarkButtonsDirty( ); + RenderButtons( ); + + // render text boxes + RenderAllTextFields(); // textbox system call + + // render help +// RenderFastHelp( ); +// RenderButtonsFastHelp( ); + + + ExecuteBaseDirtyRectQueue(); + EndFrameBufferRender(); + + // handle fades in and out + if ( HandleFadeOutCallback( ) ) + { + ClearMainMenu(); + return( gubMPSExitScreen ); + } + + if ( HandleBeginFadeOut( gubMPSExitScreen ) ) + { + return( gubMPSExitScreen ); + } + + if ( HandleFadeInCallback( ) ) + { + // Re-render the scene! + RenderMPSScreen(); + } + + if ( HandleBeginFadeIn( gubMPSExitScreen ) ) + { + + } + + if( gfMPSScreenExit ) // we are exiting this screen + { + ExitMPSScreen(); // perform destruction + } + + return( gubMPSExitScreen ); +} // end MPScoreScreenHandle() + +UINT32 MPScoreScreenShutdown( void ) +{ + return( 1 ); +} + +BOOLEAN EnterMPSScreen() +{ + VOBJECT_DESC VObjectDesc; + UINT16 cnt; + UINT16 usPosY; + + if( gfMPSButtonsAllocated ) + return( TRUE ); + + SetCurrentCursorFromDatabase( CURSOR_NORMAL ); + + // load the Main trade screen backgroiund image + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_800x600.sti", VObjectDesc.ImageFile); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\OptionsScreenBackGround_1024x768.sti", VObjectDesc.ImageFile); + } + + CHECKF(AddVideoObject(&VObjectDesc, &guiMPSMainBackGroundImage )); + + //Cancel button + giMPSCancelBtnImage = LoadButtonImage("INTERFACE\\PreferencesButtons.sti", -1,0,-1,2,-1 ); + guiMPSCancelButton = CreateIconAndTextButton( giMPSCancelBtnImage, gzMPSScreenText[MPS_CANCEL_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPS_BTN_CANCEL_X, MPS_BTN_CANCEL_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPSCancelCallback); + + SpecifyButtonSoundScheme( guiMPSCancelButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPSCancelButton, DISABLED_STYLE_NONE ); + + //Continue button + giMPSContinueBtnImage = UseLoadedButtonImage( giMPSCancelBtnImage, -1,1,-1,3,-1 ); + guiMPSContinueButton = CreateIconAndTextButton( giMPSContinueBtnImage, gzMPSScreenText[MPS_CONTINUE_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPS_BTN_CONTINUE_X, MPS_BTN_CONTINUE_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPSContinueCallback); + + SpecifyButtonSoundScheme( guiMPSContinueButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPSContinueButton, DISABLED_STYLE_NONE ); + + //Reset the exit screen - screen the main game loop will call next iteration + gubMPSExitScreen = MP_SCORE_SCREEN; + + //REnder the screen once so we can blt ot to ths save buffer + RenderMPSScreen(); + + BlitBufferToBuffer(guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + + gfMPSButtonsAllocated = TRUE; + + return( TRUE ); + +} // End of EnterMPSScreen() + + +BOOLEAN ExitMPSScreen() +{ + UINT16 cnt; + + if( !gfMPSButtonsAllocated ) + return( TRUE ); + + //Delete the main options screen background + DeleteVideoObjectFromIndex( guiMPSMainBackGroundImage ); + + RemoveButton( guiMPSContinueButton ); + RemoveButton( guiMPSCancelButton ); + + + UnloadButtonImage( giMPSCancelBtnImage ); + UnloadButtonImage( giMPSContinueBtnImage ); + + // exit text input mode in this screen and clean up text boxes + //KillAllTextInputModes(); + SetTextInputCursor( CURSOR_IBEAM ); + + gfMPSButtonsAllocated = FALSE; + + //If we are starting the game stop playing the music + if( gubMPSScreenHandler == MPS_CONTINUE ) + SetMusicMode( MUSIC_NONE ); + + gfMPSScreenExit = FALSE; + gfMPSScreenEntry = TRUE; + + return( TRUE ); + +} // End of ExitMPJScreen() + + +void HandleMPSScreen() +{ + if( gubMPSScreenHandler != MPS_NOTHING ) + { + switch( gubMPSScreenHandler ) + { + case MPS_CANCEL: + gubMPSExitScreen = MAINMENU_SCREEN; + gfMPSScreenExit = TRUE; + break; + + /*case MPJ_HOST: + gubMPJExitScreen = MP_HOST_SCREEN; + gfMPJScreenExit = TRUE; + break;*/ + + + case MPS_CONTINUE: + { + //if we are already fading out, get out of here + if( gFadeOutDoneCallback != DoneFadeOutForExitMPSScreen ) + { + //Disable the ok button + DisableButton( guiMPSCancelButton ); + DisableButton( guiMPSContinueButton ); + + gFadeOutDoneCallback = DoneFadeOutForExitMPSScreen; + + FadeOutNextFrame( ); + } + break; + } + + } + + gubMPSScreenHandler = MPS_NOTHING; + } + + + if( gfReRenderMPSScreen ) + { + RenderMPSScreen(); + gfReRenderMPSScreen = FALSE; + } + + // add restore backgrounds in... + //RestoreGIOButtonBackGrounds(); +} // end of HandleMPJScreen + + +BOOLEAN RenderMPSScreen() +{ + HVOBJECT hPixHandle; + UINT16 usPosY; + + //Get the main background screen graphic and blt it + GetVideoObject(&hPixHandle, guiMPSMainBackGroundImage ); + BltVideoObject(FRAME_BUFFER, hPixHandle, 0,0,0, VO_BLT_SRCTRANSPARENCY,NULL); + + //Shade the background + ShadowVideoSurfaceRect( FRAME_BUFFER, iScreenWidthOffset, iScreenHeightOffset, iScreenWidthOffset + 640, iScreenHeightOffset + 480 ); + + //Display the title + DrawTextToScreen( gzMPSScreenText[ MPS_TITLE_TEXT ], MPS_MAIN_TITLE_X, MPS_MAIN_TITLE_Y, MPS_MAIN_TITLE_WIDTH, MPS_TITLE_FONT, MPS_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + + // Player Column + DisplayWrappedString( MPS_LABEL_PLAYER_X, MPS_LABEL_PLAYER_Y, MPS_LABEL_PLAYER_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_PLAYER_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + // Kills Column + DisplayWrappedString( MPS_LABEL_KILLS_X, MPS_LABEL_KILLS_Y, MPS_LABEL_KILLS_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_KILLS_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + // Deaths Column + DisplayWrappedString( MPS_LABEL_DEATHS_X, MPS_LABEL_DEATHS_Y, MPS_LABEL_DEATHS_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_DEATHS_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + // Hits Column + DisplayWrappedString( MPS_LABEL_HITS_X, MPS_LABEL_HITS_Y, MPS_LABEL_HITS_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_HITS_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + // Misses Column + DisplayWrappedString( MPS_LABEL_MISSES_X, MPS_LABEL_MISSES_Y, MPS_LABEL_MISSES_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_MISSES_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + // Accuracy Column + DisplayWrappedString( MPS_LABEL_ACCURACY_X, MPS_LABEL_ACCURACY_Y, MPS_LABEL_ACCURACY_WIDTH, 2, MPS_TITLE_FONT, MPS_TITLE_COLOR, gzMPSScreenText[ MPS_ACCURACY_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw players + wchar_t szPlayerName[30]; + wchar_t szPlayerKills[3]; + wchar_t szPlayerDeaths[3]; + wchar_t szPlayerHits[3]; + wchar_t szPlayerMisses[3]; + wchar_t szPlayerAccuracy[8]; + float flAccuracy = 0; + for(int i=0; i < 4; i++) + { + if (client_names[i] != NULL) + { + if (strcmp(client_names[i],"")!=0) + { + // valid player + usPosY = MPS_PLAYERLIST_Y + (i * MPS_PLAYER_GAP); + + // Draw Player Name Column + memset(szPlayerName,0,30*sizeof(wchar_t)); + mbstowcs( szPlayerName,client_names[i],30); + + DisplayWrappedString( MPS_LABEL_PLAYER_X, usPosY, MPS_LABEL_PLAYER_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerName, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Kills Column + memset(szPlayerKills,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[i].kills,szPlayerKills,10); + DisplayWrappedString( MPS_LABEL_KILLS_X, usPosY, MPS_LABEL_KILLS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerKills, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Deaths Column + memset(szPlayerDeaths,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[i].deaths,szPlayerDeaths,10); + DisplayWrappedString( MPS_LABEL_DEATHS_X, usPosY, MPS_LABEL_DEATHS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerDeaths, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Hits Column + memset(szPlayerHits,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[i].hits,szPlayerHits,10); + DisplayWrappedString( MPS_LABEL_HITS_X, usPosY, MPS_LABEL_HITS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerHits, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Misses Column + memset(szPlayerMisses,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[i].misses,szPlayerMisses,10); + DisplayWrappedString( MPS_LABEL_MISSES_X, usPosY, MPS_LABEL_MISSES_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerMisses, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Accuracy Column + if ( gMPPlayerStats[i].misses + gMPPlayerStats[i].hits > 0 ) + flAccuracy = ((float)gMPPlayerStats[i].hits / (float)(gMPPlayerStats[i].misses + gMPPlayerStats[i].hits)) * 100.0f; + else + flAccuracy = 0; + memset(szPlayerAccuracy,0,8*sizeof(wchar_t)); + swprintf(szPlayerAccuracy,L"%i%%%%%%%%",(int)flAccuracy); // this thing goes through like three printfs before being rendered.... + DisplayWrappedString( MPS_LABEL_ACCURACY_X, usPosY, MPS_LABEL_ACCURACY_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerAccuracy, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + } + + } + } + + if (PLAYER_BSIDE==MP_TYPE_COOP) + { + // CO-OP Mode, show stats for queens army + usPosY = MPS_PLAYERLIST_Y + (5 * MPS_PLAYER_GAP); // leave a space between players and AI + + // Draw AI Team Name + DisplayWrappedString( MPS_LABEL_PLAYER_X, usPosY, MPS_LABEL_PLAYER_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, gzMPSScreenText[MPS_AITEAM_TEXT], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Kills Column + memset(szPlayerKills,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[4].kills,szPlayerKills,10); + DisplayWrappedString( MPS_LABEL_KILLS_X, usPosY, MPS_LABEL_KILLS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerKills, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Deaths Column + memset(szPlayerDeaths,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[4].deaths,szPlayerDeaths,10); + DisplayWrappedString( MPS_LABEL_DEATHS_X, usPosY, MPS_LABEL_DEATHS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerDeaths, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Hits Column + memset(szPlayerHits,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[4].hits,szPlayerHits,10); + DisplayWrappedString( MPS_LABEL_HITS_X, usPosY, MPS_LABEL_HITS_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerHits, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Misses Column + memset(szPlayerMisses,0,3*sizeof(wchar_t)); + _itow(gMPPlayerStats[4].misses,szPlayerMisses,10); + DisplayWrappedString( MPS_LABEL_MISSES_X, usPosY, MPS_LABEL_MISSES_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerMisses, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Draw Accuracy Column + if ( gMPPlayerStats[4].misses + gMPPlayerStats[4].hits > 0 ) + flAccuracy = ((float)gMPPlayerStats[4].hits / (float)(gMPPlayerStats[4].misses + gMPPlayerStats[4].hits)) * 100.0f; + else + flAccuracy = 0; + memset(szPlayerAccuracy,0,8*sizeof(wchar_t)); + swprintf(szPlayerAccuracy,L"%i%%%%%%%%",(int)flAccuracy); + DisplayWrappedString( MPS_LABEL_ACCURACY_X, usPosY, MPS_LABEL_ACCURACY_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerAccuracy, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + } + +/* + // Player name text label + DisplayWrappedString( MPJ_LABEL_HANDLE_X, MPJ_LABEL_HANDLE_Y, MPJ_LABEL_HANDLE_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_HANDLE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Server IP text label + DisplayWrappedString( MPJ_LABEL_IP_X, MPJ_LABEL_IP_Y, MPJ_LABEL_IP_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_SERVERIP_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + + // Server Port text label + DisplayWrappedString( MPJ_LABEL_PORT_X, MPJ_LABEL_PORT_Y, MPJ_LABEL_PORT_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_SERVERPORT_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); +*/ + return( TRUE ); +} // end of RenderMPSScreen() + + + +void GetMPSScreenUserInput() +{ + InputAtom Event; +// POINT MousePos; + +// GetCursorPos(&MousePos); + + while( DequeueEvent( &Event ) ) + { + // check if this event is swallowed by text input, otherwise process key + if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN ) + { + switch( Event.usParam ) + { + + case ESC: + //Exit out of the screen + gubMPSScreenHandler = MPS_CANCEL; + break; + + case ENTER: + /*if (ValidateJoinSettings(false)) + { + SaveJoinSettings(); + gubMPSScreenHandler = MPS_JOIN; + }*/ + gubMPSScreenHandler = MPS_CONTINUE; + break; + } + } + } +} // end of GetMPSScreenUserInput() + +void DoneFadeOutForExitMPSScreen( void ) +{ + // As we bypassed the GIO screen, set up some game options for multiplayer here + // most things i have left as thier defaults here for testing. + is_networked = true; + + ReStartingGame(); + + if (is_client) + { + client_disconnect(); + } + + if(is_server) + { + server_disconnect(); + } + + //NetworkAutoStart will handle reconnection in Map Screen + // reset client auto_retry for NetworkAutoStart called in MapScreenHandle + auto_retry = true; + giNumTries = 5; + + if (is_networked) + gGameOptions.fTurnTimeLimit = TRUE; + else + gGameOptions.fTurnTimeLimit = FALSE; + + // Bobby Rays - why would we want anything less than the best + gGameOptions.ubBobbyRay = BR_AWESOME; + + + gubMPSExitScreen = INTRO_SCREEN; + + //set the fact that we should do the intro videos +// gbIntroScreenMode = INTRO_BEGINING; +#ifdef JA2TESTVERSION + if( gfKeyState[ ALT ] ) + { + if( gfKeyState[ CTRL ] ) + { + gMercProfiles[ MIGUEL ].bMercStatus = MERC_IS_DEAD; + gMercProfiles[ SKYRIDER ].bMercStatus = MERC_IS_DEAD; + } + + SetIntroType( INTRO_ENDING ); + } + else +#endif + SetIntroType( INTRO_BEGINING ); + + ExitMPSScreen(); // cleanup please, if we called a fadeout then we didnt do it above + + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void DoneFadeInForExitMPSScreen( void ) +{ + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void BtnMPSCancelCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + gubMPSScreenHandler = MPS_CANCEL; + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + +} + +void BtnMPSContinueCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + gubMPSScreenHandler = MPS_CONTINUE; + + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } +} \ No newline at end of file diff --git a/MPScoreScreen.h b/MPScoreScreen.h new file mode 100644 index 00000000..70043248 --- /dev/null +++ b/MPScoreScreen.h @@ -0,0 +1,12 @@ +#ifndef _MP_SCORE_SCREEN_H_ +#define _MP_SCORE_SCREEN_H_ + + +UINT32 MPScoreScreenInit( void ); +UINT32 MPScoreScreenHandle( void ); +UINT32 MPScoreScreenShutdown( void ); + + + + +#endif \ No newline at end of file diff --git a/MainMenuScreen.cpp b/MainMenuScreen.cpp index 32cc0686..391475c5 100644 --- a/MainMenuScreen.cpp +++ b/MainMenuScreen.cpp @@ -388,6 +388,7 @@ void ExitMainMenu( ) void InitDependingGameStyleOptions(BOOLEAN isNetworked) { // Load the ja2_options.ini + FreeGameExternalOptions(); LoadGameExternalOptions(); ReStartingGame(); @@ -449,7 +450,8 @@ void MenuButtonCallback(GUI_BUTTON *btn,INT32 reason) //if something didnt work, dont even know how to make error code...//hayden } - SetMainMenuExitScreen( GAME_INIT_OPTIONS_SCREEN ); + SetMainMenuExitScreen( MP_JOIN_SCREEN ); // OJW - 20081129 + //SetMainMenuExitScreen( GAME_INIT_OPTIONS_SCREEN ); } else if( gbHandledMainMenu == LOAD_GAME ) { diff --git a/MessageBoxScreen.cpp b/MessageBoxScreen.cpp index c60a3c26..93c84ff7 100644 --- a/MessageBoxScreen.cpp +++ b/MessageBoxScreen.cpp @@ -23,6 +23,7 @@ #include "cursor control.h" #include "laptop.h" #include "text.h" + #include "Text Input.h" #include "overhead map.h" #endif @@ -49,6 +50,8 @@ BOOLEAN gfInMsgBox = FALSE; extern BOOLEAN fInMapMode; extern BOOLEAN gfOverheadMapDirty; +//OJW - 20090208 +CHAR16 gszMsgBoxInputString[255]; void OKMsgBoxCallback(GUI_BUTTON *btn, INT32 reason ); void YESMsgBoxCallback(GUI_BUTTON *btn, INT32 reason ); @@ -350,6 +353,51 @@ INT32 DoMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UIN ForceButtonUnDirty( gMsgBox.uiButton[1] ); ForceButtonUnDirty( gMsgBox.uiButton[0] ); + } + else if (usFlags & MSG_BOX_FLAG_INPUTBOX) + { + // Initialise Text Boxes + InitTextInputMode(); // API call to initialise text input mode for this screen + // does not mean we are inputting text right away + + // Player Name field + SetTextInputCursor( CUROSR_IBEAM_WHITE ); + SetTextInputFont( (UINT16) FONT12ARIALFIXEDWIDTH ); //FONT12ARIAL //FONT12ARIALFIXEDWIDTH + Set16BPPTextFieldColor( Get16BPPColor(FROMRGB( 0, 0, 0) ) ); + SetBevelColors( Get16BPPColor(FROMRGB(136, 138, 135)), Get16BPPColor(FROMRGB(24, 61, 81)) ); + SetTextInputRegularColors( FONT_WHITE, 2 ); + SetTextInputHilitedColors( 2, FONT_WHITE, FONT_WHITE ); + SetCursorColor( Get16BPPColor(FROMRGB(255, 255, 255) ) ); + + int ibx = gMsgBox.sX + 10; + int iby = gMsgBox.sY +(usTextBoxHeight - 20 - 10); + //Add Player Name textbox + AddTextInputField( ibx, + iby, + usTextBoxWidth - 20, + 20, + MSYS_PRIORITY_HIGH+2, + gszMsgBoxInputString, + 255, + INPUTTYPE_ASCII );//23 + + // exit text input mode in this screen and clean up text boxes + SetActiveField( 0 ); + + // initialise the chat toggle boxes + /*int usPosY = gMsgBox.sY + (usTextBoxHeight - 45); + int usPosX = gMsgBox.sX + (usTextBoxWidth / 3); + + guiChatToggles[ 0 ] = CreateCheckBoxButton( usPosX, usPosY, + "INTERFACE\\OptionsCheckBoxes_12x12.sti", MSYS_PRIORITY_HIGH+10, + BtnChatTogglesCallback ); + MSYS_SetBtnUserData( guiOptionsToggles[ 0 ], 0, 0 ); + + guiChatToggles[ 1 ] = CreateCheckBoxButton( usPosX, usPosY, + "INTERFACE\\OptionsCheckBoxes_12x12.sti", MSYS_PRIORITY_HIGH+10, + BtnChatTogglesCallback ); + MSYS_SetBtnUserData( guiOptionsToggles[ 1 ], 0, 1 );*/ + } else { @@ -810,6 +858,12 @@ UINT32 ExitMsgBox( INT8 ubExitCode ) RemoveButton( gMsgBox.uiButton[2] ); RemoveButton( gMsgBox.uiButton[3] ); } + // OJW - 20090208 - Add text input box type + else if (gMsgBox.usFlags & MSG_BOX_FLAG_INPUTBOX) + { + // exit text input mode in this screen and clean up text boxes + KillAllTextInputModes(); + } else { if ( gMsgBox.usFlags & MSG_BOX_FLAG_OK ) @@ -1102,21 +1156,33 @@ UINT32 MessageBoxScreenHandle( ) // Render buttons RenderButtons( ); + if (gMsgBox.usFlags & MSG_BOX_FLAG_INPUTBOX) + { + // render text boxes + RenderAllTextFields(); // textbox system call + } EndFrameBufferRender( ); // carter, need key shortcuts for clearing up message boxes // Check for esc while (DequeueEvent(&InputEvent) == TRUE) { - if( InputEvent.usEvent == KEY_UP ) + if( !HandleTextInput( &InputEvent ) && InputEvent.usEvent == KEY_DOWN ) { if( ( InputEvent.usParam == ESC ) || ( InputEvent.usParam == 'n') ) { - if ( gMsgBox.usFlags & MSG_BOX_FLAG_YESNO ) - { - // Exit messagebox - gMsgBox.bHandled = MSG_BOX_RETURN_NO; - } + if ( gMsgBox.usFlags & MSG_BOX_FLAG_YESNO ) + { + // Exit messagebox + gMsgBox.bHandled = MSG_BOX_RETURN_NO; + } + //OJW - 20090208 - Input Box + else if( gMsgBox.usFlags & MSG_BOX_FLAG_INPUTBOX ) + { + // Exit messagebox + gMsgBox.bHandled = MSG_BOX_RETURN_NO; + memset(gszMsgBoxInputString,0,sizeof(CHAR16)*255); + } } if( InputEvent.usParam == ENTER ) @@ -1136,6 +1202,14 @@ UINT32 MessageBoxScreenHandle( ) // Exit messagebox gMsgBox.bHandled = MSG_BOX_RETURN_OK; } + //OJW - 20090208 - Input Box + else if( gMsgBox.usFlags & MSG_BOX_FLAG_INPUTBOX ) + { + // retrieve the string from the text box + Get16BitStringFromField( 0, gszMsgBoxInputString ); // these indexes are based on the order created + // Exit messagebox + gMsgBox.bHandled = MSG_BOX_RETURN_OK; + } } if( InputEvent.usParam == 'o' ) { diff --git a/MessageBoxScreen.h b/MessageBoxScreen.h index 66730059..cb7109af 100644 --- a/MessageBoxScreen.h +++ b/MessageBoxScreen.h @@ -16,6 +16,8 @@ #define MSG_BOX_FLAG_OKSKIP 0x0200 // Displays ok or skip (meanwhile) buttons #define MSG_BOX_FLAG_GENERICCONTRACT 0x0400 // displays contract buttoin + 2 user-defined text buttons #define MSG_BOX_FLAG_GENERIC 0x0800 // 2 user-defined text buttons +// OJW - Adding text chatbox +#define MSG_BOX_FLAG_INPUTBOX 0x1000 // has a text input field // message box return codes #define MSG_BOX_RETURN_OK 1 // ENTER or on OK button @@ -83,6 +85,8 @@ extern BOOLEAN fRestoreBackgroundForMessageBox; //this variable can be unset if ur in a non gamescreen and DONT want the msg box to use the save buffer extern BOOLEAN gfDontOverRideSaveBuffer; +//OJW - 20090208 +extern CHAR16 gszMsgBoxInputString[255]; //////////////////////////////// // ubStyle: Determines the look of graphics including buttons // zString: 16-bit string diff --git a/Multiplayer/client.cpp b/Multiplayer/client.cpp index dd4e0ac7..28ecf3e2 100644 --- a/Multiplayer/client.cpp +++ b/Multiplayer/client.cpp @@ -66,6 +66,9 @@ #include "BobbyRMailOrder.h" #include "Finances.h" #include "TeamTurns.h" + #include "gameloop.h" + #include "Options Screen.h" +#include "MPChatScreen.h" #endif #include "MessageIdentifiers.h" @@ -92,7 +95,7 @@ unsigned char GetPacketIdentifier(Packet *p); unsigned char packetIdentifier; -#include "messagebox.h" +#include "MessageBoxScreen.h" #pragma pack(1) @@ -116,6 +119,7 @@ unsigned char packetIdentifier; #include "interface panels.h" #include "game init.h" +#include "Debug Control.h" extern INT8 SquadMovementGroups[ ]; RakPeerInterface *client; @@ -182,7 +186,6 @@ typedef struct UINT8 client_num; bool status; UINT8 ready_stage; - } ready_struct; typedef struct @@ -197,9 +200,24 @@ typedef struct UINT8 ubResult; }kickR; +typedef struct +{ + UINT8 client_num; + BOOLEAN bToAll; + CHAR16 msg[300]; +} chat_msg; + bullets_table bTable[11][50]; char client_names[4][30]; +// OJW - 20081204 +int client_ready[4]; +int client_edges[4]; +int client_teams[4]; +int random_mercs[7]; + +// OJW - 20081222 +player_stats gMPPlayerStats[5]; INT32 MAX_MERCS; @@ -219,6 +237,11 @@ UINT8 ubID_prefix; bool is_server=false; bool is_networked=false; + bool is_host=false; // OJW - added 20081130 - new flag to signal our intention to host, coming in from the HOST screen + bool auto_retry=true; + int giNumTries = 5; // default is 5 retries + UINT32 giNextRetryTime = 0; + bool requested=false; int tcount; @@ -236,6 +259,10 @@ bool DISABLE_MORALE; char SECT_EDGE[30]; char ckbag[100]; char CLIENT_NAME[30]; + // OJW - 20081204 + char SERVER_NAME[30]; + int RANDOM_SPAWN; + int RANDOM_MERCS; char SERVER_IP[30] ; char SERVER_PORT[30]; @@ -279,6 +306,95 @@ INT16 crate_sGridX, crate_sGridY; void overide_callback( UINT8 ubResult ); void kick_callback( UINT8 ubResult ); void turn_callback (UINT8 ubResult); +void ChatCallback (UINT8 ubResult); + +// OJW - 20081222 +void send_gameover( void ); +void StartScoreScreen(); // this screen will send us to the multiplayer score screen +bool is_game_over = false; +UINT32 iScoreScreenTime = 0; +int iTeamsWiped = 0; // counts how many teams have wiped + +//OJW - 20090210 +int iDisconnectedScreen = 0; + +//OJW - 20090302 +int bClosingChatBoxToStartGame = false; +UINT32 iCCStartGameTime = 0; + +// OJW - 20090317 +bool is_game_started = false; + + +// OJW - added 20081130 +// - add retry timer and notification +void NetworkAutoStart() +{ + if (!is_networked) + return; // not networked, bad call + + if (!is_host || is_server) + { + // is pure client or the server has been started + // so do client connection checking...though probably not needed for the server client. cant hurt. + + // user JOIN'd game + if (is_client && is_connected) + { + return; // should be set up and running + } + else if (is_connecting) + { + return; // we're waiting on a connection , do nothing + } + else + { + if (giNumTries <= 0 || !auto_retry) + return; // dont auto-retry + + if (guiBaseJA2NoPauseClock < giNextRetryTime) + return; // we are waiting for a retry timer + + // try and connect + giNumTries--; + connect_client(); + + // next rety time is set in client_packet() + } + } + else + { + // is host and server isnt started so start it + start_server(); + } +} + +void HireRandomMercs() +{ + MERC_HIRE_STRUCT HireMercStruct; + for (int i=0; i < MAX_MERCS; i++) + { + memset(&HireMercStruct, 0, sizeof(MERC_HIRE_STRUCT)); + + HireMercStruct.ubProfileID = random_mercs[i];// use the merc list recieved from the server + + //DEF: temp + HireMercStruct.sSectorX = gsMercArriveSectorX; + HireMercStruct.sSectorY = gsMercArriveSectorY; + HireMercStruct.fUseLandingZoneForArrival = TRUE; + HireMercStruct.ubInsertionCode = INSERTION_CODE_ARRIVING_GAME; + HireMercStruct.iTotalContractLength = 1; + HireMercStruct.fCopyProfileItemsOver = true; + + gMercProfiles[ HireMercStruct.ubProfileID ].ubMiscFlags |= PROFILE_MISC_FLAG_ALREADY_USED_ITEMS; + + HireMerc(&HireMercStruct); + } + + fDrawCharacterList = true; + fTeamPanelDirty = true; + ReBuildCharactersList(); +} //***************** @@ -781,10 +897,11 @@ void send_EndTurn( UINT8 ubNextTeam ) { //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"sendEndTurn" ); - if(ubNextTeam==0) - { - ubNextTeam=netbTeam; - } + if(ubNextTeam==0) + { + // translate next team id for clients + ubNextTeam=netbTeam; + } turn_struct tStruct; @@ -802,22 +919,23 @@ void send_EndTurn( UINT8 ubNextTeam ) void recieveEndTurn(RPCParameters *rpcParameters) { - turn_struct* tStruct = (turn_struct*)rpcParameters->input; - //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"recieveEndTurn" ); - UINT8 sender_bTeam; - UINT8 ubTeam; - sender_bTeam=tStruct->tsnetbTeam; - ubTeam=tStruct->tsubNextTeam; - if(is_server)Sawarded=false; + turn_struct* tStruct = (turn_struct*)rpcParameters->input; + //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"recieveEndTurn" ); + UINT8 sender_bTeam; + UINT8 ubTeam; + sender_bTeam=tStruct->tsnetbTeam; + ubTeam=tStruct->tsubNextTeam; + if(is_server)Sawarded=false; + // if the message was recieved from the server.. if(is_server || sender_bTeam==6) { + // if not the server and we're not in combat... if (!is_server && !(gTacticalStatus.uiFlags & INCOMBAT)) { EnterCombatMode(0); } - if(ubTeam==netbTeam)ubTeam=0; { if(!is_server && is_client) @@ -988,6 +1106,9 @@ void send_ready ( void ) status=1; numready = numready+1; ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[7],numready,MAX_CLIENTS ); + // OJW - 20081204 + client_ready[info.client_num-1]=1; + fDrawCharacterList = true; // set the character list to be redrawn } else { @@ -995,6 +1116,9 @@ void send_ready ( void ) status=0; numready = numready-1; ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[8],numready,MAX_CLIENTS ); + // OJW - 20081204 + client_ready[info.client_num-1]=0; + fDrawCharacterList = true; // set the character list to be redrawn } } @@ -1044,13 +1168,17 @@ void recieveREADY (RPCParameters *rpcParameters) { numready = numready+1; ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); - + // OJW - 20081204 + client_ready[info->client_num-1]=1; + fDrawCharacterList = true; // set the character list to be redrawn } else { numready = numready-1; ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[11], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); - + // OJW - 20081204 + client_ready[info->client_num-1]=0; + fDrawCharacterList = true; // set the character list to be redrawn } if(is_server && numready == MAX_CLIENTS) //all ready. and server tells all to load...and loads himself @@ -1066,6 +1194,18 @@ void recieveREADY (RPCParameters *rpcParameters) { ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[36] ); allowlaptop=1; + if (RANDOM_MERCS) + HireRandomMercs(); + + // server is starting the game, adjust the games max players + // this will allow the game to be started, rather than saying 2/4 players are connected + // where there are only two players connected + int iPlayersConnected = 0; + for (int i=0; i< 4; i++) + if (client_names[i] != NULL && strcmp(client_names[i],"") != 0) + iPlayersConnected++; + + MAX_CLIENTS = iPlayersConnected; } } @@ -1217,78 +1357,131 @@ void allowlaptop_callback ( UINT8 ubResult ) info.ready_stage=36; info.status=1; + if (RANDOM_MERCS) + HireRandomMercs(); + + // server client is starting the game, adjust the games max players + // this will allow the game to be started, rather than saying 2/4 players are connected + // where there are only two players connected + int iPlayersConnected = 0; + for (int i=0; i< 4; i++) + if (client_names[i] != NULL && strcmp(client_names[i],"") != 0) + iPlayersConnected++; + + MAX_CLIENTS = iPlayersConnected; + client->RPC("sendREADY",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } } +void StartBattleChatBoxClosedCallback(void) +{ + // The chat box has been closed, now we can start + SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ ); + is_game_started = true; +} + + void start_battle ( void ) { -if(!is_client) -{ -ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[14] ); -} -else if(!allowlaptop && is_server) -{ - SGPRect CenterRect = { 100, 100, SCREEN_WIDTH - 100, 300 }; - DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[35], guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowlaptop_callback, &CenterRect ); - -} -else if(allowlaptop) -{ - - if ( NumberOfMercsOnPlayerTeam() ==0) + if(!is_client) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[15] ); + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[14] ); } - else if(goahead==1) - { - goahead=0; - status=0;//reset - numready=0; - SOLDIERTYPE *pSoldier = MercPtrs[ 0 ]; - UINT8 ubGroupID = pSoldier->ubGroupID; + else if(!allowlaptop && is_server) + { + // check that another player is actually connected + int iPlayersConnected = 0; + for (int i=0; i< 4; i++) + if (client_names[i] != NULL && strcmp(client_names[i],"") != 0) + iPlayersConnected++; - GROUP *pGroup; - pGroup = GetGroup( ubGroupID ); - gpBattleGroup = pGroup; - gubPBSectorX = gpBattleGroup->ubSectorX; - gubPBSectorY = gpBattleGroup->ubSectorY; - gubPBSectorZ = gpBattleGroup->ubSectorZ; - - gfEnterTacticalPlacementGUI = 1; - - UINT32 i; - for(i=0; i<4;i++) - { - CHAR16 name[255]; - int nm = mbstowcs( name, client_names[i], sizeof (char)*30 ); - //copy in client specified name for the player turn bar :) - if(nm) + if (iPlayersConnected > 1) { - CHAR16 string[255]; - memcpy(string,TeamTurnString[ (i+6) ], sizeof( CHAR16) * 255 ); + SGPRect CenterRect = { 100, 100, SCREEN_WIDTH - 100, 300 }; + DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[35], guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowlaptop_callback, &CenterRect ); + } + else + { + // notify the server that at least one other player must be connected + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[51] ); + } + + } + else if(allowlaptop) + { + if ( NumberOfMercsOnPlayerTeam() ==0) + { + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[15] ); + } + else if(goahead==1) + { + goahead=0; // this ensures that we dont reload the sector again the next time this function is called - CHAR16 full[255]; - swprintf(full, L"%s - '%s'",string,name); + if (is_game_started) + return; // 20090317 - OJW - there is a bug or out of sync condition that rarely somestimes causes a client to reach here + // twice, and reload the sector, which causes an assertion failure on line 1782 of strategicmap.cpp in SetCurrentWorldSector() + // therefore i'm adding this to make sure if they client loaded the map once, it wont do it again + + status=0;//reset + numready=0; + SOLDIERTYPE *pSoldier = MercPtrs[ 0 ]; + UINT8 ubGroupID = pSoldier->ubGroupID; - memcpy( TeamTurnString[ (i+6) ] , full, sizeof( CHAR16) * 255 ); + GROUP *pGroup; + pGroup = GetGroup( ubGroupID ); + gpBattleGroup = pGroup; + gubPBSectorX = gpBattleGroup->ubSectorX; + gubPBSectorY = gpBattleGroup->ubSectorY; + gubPBSectorZ = gpBattleGroup->ubSectorZ; + + gfEnterTacticalPlacementGUI = 1; + // OJW - 20090205 + iTeamsWiped = 0; // reset the number of wiped teams to Zero + + UINT32 i; + for(i=0; i<4;i++) + { + CHAR16 name[255]; + int nm = mbstowcs( name, client_names[i], sizeof (char)*30 ); + //copy in client specified name for the player turn bar :) + if(nm) + { + // OJW - 20090318 - fixed name copying bug with multiple games + CHAR16 full[255]; + swprintf(full, MPClientMessage[57],i+1,name); + + memcpy( TeamTurnString[ (i+6) ] , full, sizeof( CHAR16) * 255 ); + } + } + + // this closes the chat window if its open and the game is starting + // if this is open then the client will crash when the screen returns + if (guiCurrentScreen == MP_CHAT_SCREEN) + { + gChatBox.bHandled = MSG_BOX_RETURN_NO; + bClosingChatBoxToStartGame = true; + iCCStartGameTime = guiBaseJA2NoPauseClock+1000; + } + else + { + SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ ); + is_game_started = true; + } + + } + else + { + send_ready(); } } - - SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ ); - } - else + else if(!allowlaptop && is_client && !is_server) { - send_ready(); + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[16] ); } -} -else if(!allowlaptop && is_client && !is_server) -{ - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[16] ); -} } @@ -1797,15 +1990,22 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve settings_struct* cl_lan = (settings_struct*)rpcParameters->input; - char szDefault[30]; - sprintf(szDefault, "%s",cl_lan->client_name); + char szDefault[30]; + sprintf(szDefault, "%s",cl_lan->client_name); - if(!recieved_settings) + // OJW - 20081204 + // get complete client data from the server + memcpy( client_edges, cl_lan->client_edges , sizeof(int) * 4); + memcpy( client_teams, cl_lan->client_teams , sizeof(int) * 4); + + if(!recieved_settings && strcmp(cl_lan->client_name, CLIENT_NAME)==0) { + // This settings packet contains information and settings specifically for us recieved_settings=1; CLIENT_NUM=cl_lan->client_num;//assign client number from server + netbTeam = (CLIENT_NUM)+5; ubID_prefix = gTacticalStatus.Team[ netbTeam ].bFirstID;//over here now @@ -1813,7 +2013,23 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve strcpy(client_names[cl_lan->client_num-1],szDefault); - + // OJW - 20081204 + strcpy(SERVER_NAME,cl_lan->server_name); + RANDOM_MERCS = cl_lan->RANDOM_MERCS; + RANDOM_SPAWN = cl_lan->RANDOM_SPAWN; + + if (RANDOM_SPAWN) + { + // store sector edge + _itoa(cl_lan->cl_edge,SECT_EDGE,10); + } + + if(RANDOM_MERCS) + { + // copy the random merc list locally + memcpy(random_mercs,cl_lan->random_mercs,sizeof(int) * 7); + } + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[2] ); @@ -1957,8 +2173,14 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve // WANNE - MP: We also have to reinitialize the merc profiles because // they depend on the inventory! LoadMercProfiles(); - - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,cl_lan->cl_edge,cl_lan->team ); + + if (!RANDOM_SPAWN) + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,cl_lan->cl_edge,cl_lan->team ); + else + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,L"?",cl_lan->team ); + + fDrawCharacterList = true; // set the character list to be redrawn + // if(PLAYER_BSIDE==0)ScreenMsg(FONT_LTGREEN,MSG_CHAT,MPClientMessage[27],cl_lan->cl_ops[0],cl_lan->cl_ops[1],cl_lan->cl_ops[2],cl_lan->cl_ops[3]); //ScreenMsg(FONT_LTGREEN,MSG_CHAT,MPClientMessage[27],cl_lan->team); @@ -1967,9 +2189,13 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve } else { + fDrawCharacterList = true; // set the character list to be redrawn - - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,cl_lan->cl_edge,cl_lan->team ); + if (!RANDOM_SPAWN) + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,cl_lan->cl_edge,cl_lan->team ); + else + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault,L"?",cl_lan->team ); + // ScreenMsg(FONT_LTGREEN,MSG_CHAT,MPClientMessage[27],cl_lan->team); strcpy(client_names[cl_lan->client_num-1],szDefault); @@ -1977,6 +2203,80 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve } +void recieveTEAMCHANGE( RPCParameters *rpcParameters ) +{ + teamchange_struct* cl_lan = (teamchange_struct*)rpcParameters->input; + + if (!can_teamchange()) + { + // error + ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveTEAMCHANGE that should not occur"); + } + else + { + // redraw the character list on the map screen + fTeamPanelDirty = true; + fDrawCharacterList = true; + client_teams[cl_lan->client_num-1] = cl_lan->newteam; + if (cl_lan->client_num == CLIENT_NUM) + { + TEAM = cl_lan->newteam; + } + } +} + +void recieveEDGECHANGE( RPCParameters *rpcParameters ) +{ + edgechange_struct* cl_lan = (edgechange_struct*)rpcParameters->input; + + if (!can_edgechange()) + { + // error + ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveEDGECHANGE that should not occur"); + } + else + { + // redraw the character list on the map screen + fTeamPanelDirty = true; + fDrawCharacterList = true; + client_edges[cl_lan->client_num-1] = cl_lan->newedge; + if (cl_lan->client_num == CLIENT_NUM) + { + // store the setting locally + _itoa(cl_lan->newedge,SECT_EDGE,10); + } + } +} + + +void recieveMAPCHANGE( RPCParameters *rpcParameters ) +{ + mapchange_struct* cl_lan = (mapchange_struct*)rpcParameters->input; + + if (!is_client || allowlaptop) + { + // error + ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveMAPCHANGE that should not occur"); + } + else + { + // copy new map settings locally + gsMercArriveSectorX=cl_lan->gsMercArriveSectorX; + gsMercArriveSectorY=cl_lan->gsMercArriveSectorY; + + + ChangeSelectedMapSector( gsMercArriveSectorX, gsMercArriveSectorY, 0 ); + + gGameExternalOptions.iGameStartingTime= NUM_SEC_IN_DAY + int(cl_lan->TIME*3600); + + CHAR16 str[128]; + GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE ); + + // notify clients of map change in console + ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[46],str); + } +} + void send_bullet( BULLET * pBullet,UINT16 usHandItem ) { @@ -2095,34 +2395,101 @@ void send_death( SOLDIERTYPE *pSoldier ) death_struct nDeath; nDeath.soldier_id=pSoldier->ubID; nDeath.attacker_id=pSoldier->ubAttackerID; - if(pSoldier->ubAttackerID==NULL)nDeath.attacker_id=pSoldier->ubPreviousAttackerID; - if(pSoldier->ubID<20)nDeath.soldier_id=nDeath.soldier_id+ubID_prefix; - - client->RPC("sendDEATH",(const char*)&nDeath, (int)sizeof(death_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); - + + // Translate soldier id for other clients if the soldier was one of ours + if(pSoldier->ubID<20)nDeath.soldier_id=nDeath.soldier_id+ubID_prefix; + + + // if soldier died from bleeding + if(pSoldier->ubAttackerID==NULL || pSoldier->ubAttackerID == NOBODY) + { + if (pSoldier->ubPreviousAttackerID != NOBODY && pSoldier->ubPreviousAttackerID != NULL) + nDeath.attacker_id = pSoldier->ubPreviousAttackerID; + else if (pSoldier->ubNextToPreviousAttackerID != NOBODY && pSoldier->ubNextToPreviousAttackerID != NULL) + nDeath.attacker_id = pSoldier->ubNextToPreviousAttackerID; + } + SOLDIERTYPE * pAttacker=MercPtrs[ nDeath.attacker_id ]; INT8 pA_bTeam; CHAR16 pA_name[ 10 ]; INT8 pS_bTeam; CHAR16 pS_name[ 10 ]; + + // OJW - 20081222 + // save stats if(pAttacker) { - pA_bTeam=pAttacker->bTeam; - memcpy(pA_name,pAttacker->name,sizeof(CHAR16)*10); - if(pA_bTeam>5)pA_bTeam=pA_bTeam-5; - if(pA_bTeam==0)pA_bTeam=CLIENT_NUM; + // if attacker was one of our own mercs, use the last hostile attacker as the killer if there is one + if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubPreviousAttackerID != NULL && pSoldier->ubPreviousAttackerID != NOBODY) + { + pAttacker=MercPtrs[ pSoldier->ubPreviousAttackerID ]; + // check if the new attacker was also a friendly... + if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubNextToPreviousAttackerID != NULL && pSoldier->ubNextToPreviousAttackerID != NOBODY) + pAttacker=MercPtrs[ pSoldier->ubNextToPreviousAttackerID ]; + // if its still a friendly, use the original attacker id...for posterity + // guy must snore too loudly if all his mates wanna kill him :) + if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubAttackerID != NULL && pSoldier->ubAttackerID != NOBODY) + pAttacker = MercPtrs[ pSoldier->ubAttackerID ]; + + nDeath.attacker_id = pAttacker->ubID; + } + + + + // Translate attacker id for other clients if attacker was one of ours + if(pAttacker->ubID<20)nDeath.attacker_id=pAttacker->ubID+ubID_prefix; + + pA_bTeam=pAttacker->bTeam; + memcpy(pA_name,pAttacker->name,sizeof(CHAR16)*10); + if (pA_bTeam==1 && PLAYER_BSIDE==MP_TYPE_COOP) + { + // CO-OP Kill by an enemy ai + pA_bTeam = 5; // the server subtracts 1 from these numbers to score in scoreboard, AI is index 4 + } + else + { + // Any mode, kill by a players merc + if(pA_bTeam>5)pA_bTeam=pA_bTeam-5; + if(pA_bTeam==0)pA_bTeam= CLIENT_NUM; + } } if(pSoldier) { - pS_bTeam=pSoldier->bTeam; - memcpy(pS_name,pSoldier->name,sizeof(CHAR16)*10); - if(pS_bTeam>5)pS_bTeam=pS_bTeam-5; - if(pS_bTeam==0)pS_bTeam=CLIENT_NUM; + pS_bTeam=pSoldier->bTeam; + memcpy(pS_name,pSoldier->name,sizeof(CHAR16)*10); + if(pS_bTeam==1 && PLAYER_BSIDE==MP_TYPE_COOP) + { + // CO-OP Death of an enemy ai + pS_bTeam = 5; // the server subtracts 1 from these numbers to score in scoreboard, AI is index 4 + } + else + { + // Any mode death of a players merc + if(pS_bTeam>5)pS_bTeam=pS_bTeam-5; + if(pS_bTeam==0)pS_bTeam=CLIENT_NUM; + } } + + nDeath.attacker_team = pA_bTeam; + nDeath.soldier_team = pS_bTeam; + + // notify other clients of death + client->RPC("sendDEATH",(const char*)&nDeath, (int)sizeof(death_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + + // print kill notice to screen if (pSoldier->bTeam==1) ScreenMsg( FONT_YELLOW, MSG_CHAT, L"You Killed An Enemy AI"); else ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],(pS_bTeam), pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); +#if _DEBUG + char s_name[10]; + char a_name[10]; + WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL); + WideCharToMultiByte(CP_UTF8,0,pA_name,-1, a_name,10,NULL,NULL); + if (pSoldier->bTeam==1) MPDebugMsg( String ( "MPDEBUG SEND - Enemy AI #%d was killed by ('%s' - %d) (client %d - '%s')\n",nDeath.soldier_id,a_name,nDeath.attacker_id,pA_bTeam,client_names[pA_bTeam-1]) ); + else if (pAttacker->bTeam==1) MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") ); + else MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) ); +#endif + //ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],(pS_bTeam), pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); } void recieveDEATH (RPCParameters *rpcParameters) @@ -2183,6 +2550,16 @@ void recieveDEATH (RPCParameters *rpcParameters) CheckForEndOfBattle( FALSE ); } +#if _DEBUG + char s_name[10]; + char a_name[10]; + WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL); + WideCharToMultiByte(CP_UTF8,0,pA_name,-1, a_name,10,NULL,NULL); + if (pSoldier->bTeam==1) MPDebugMsg( String ( "MPDEBUG RECV - Enemy AI #%d was killed by ('%s' - #%d) (client %d - '%s')\n",nDeath->soldier_id,a_name,nDeath->attacker_id,pA_bTeam,client_names[pA_bTeam-1]) ); + else if (pAttacker->bTeam==1) MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") ); + else MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) ); +#endif + } void send_hitstruct(EV_S_STRUCTUREHIT * SStructureHit) @@ -2269,53 +2646,89 @@ BOOLEAN check_status (void)// any 'enemies' and clients left to fight ?? SOLDIERTYPE *pSoldier; int cnt; int soldiers= 0 ; + int numActiveSides = 0; + int dm_teams[4] = {0 , 0 , 0 , 0}; + for(int x=0;x < MAXTEAMS; x++) + { + soldiers=0; - for(int x=0;x < MAXTEAMS; x++) - { + for(cnt = gTacticalStatus.Team[ x ].bFirstID;cnt <= gTacticalStatus.Team[ x ].bLastID; cnt++) + { + pSoldier = MercPtrs[ cnt ]; + if(pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && pSoldier->bInSector) + { + soldiers++; + } + } + if(soldiers>0) + { + gTacticalStatus.Team[ x ].bTeamActive=1; + gTacticalStatus.Team[ x ].bMenInSector=soldiers; - for(cnt = gTacticalStatus.Team[ x ].bFirstID;cnt <= gTacticalStatus.Team[ x ].bLastID; cnt++) - { - pSoldier = MercPtrs[ cnt ]; - if(pSoldier->stats.bLife!=0 && pSoldier->bActive && pSoldier->bInSector) - { - soldiers++; - } - } - if(soldiers>0) - { - gTacticalStatus.Team[ x ].bTeamActive=1; - gTacticalStatus.Team[x].bMenInSector=soldiers; - - soldiers=0 ; - } - else - { - - gTacticalStatus.Team[ x ].bTeamActive=0; - gTacticalStatus.Team[x].bMenInSector=0; - } - } - if( (gTacticalStatus.Team[ 0 ].bTeamActive == 0) && wiped==0)//server's team has been knocked out - { - wiped=1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[40] ); + if (PLAYER_BSIDE==1 && (x==0 || x>5)) + { + // store the number of active DM teams, and number of players per team still alive + int cl_num = CLIENT_NUM; // 1 based + if (x>5) cl_num = x - 5; + dm_teams[ client_teams[ cl_num - 1 ] ]++; + } + else if (PLAYER_BSIDE != 1) + { + // count number of active teams + numActiveSides++; + } + } + else + { + gTacticalStatus.Team[ x ].bTeamActive=0; + gTacticalStatus.Team[x].bMenInSector=0; + } + } + if( (gTacticalStatus.Team[ 0 ].bTeamActive == 0) && wiped==0)//server's team has been knocked out + { + wiped=1; + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[40] ); if(!DISABLE_SPEC_MODE) { - gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;//hayden - ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[41] ); + gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;//hayden + ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[41] ); } else ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"spectator mode disabled"); - teamwiped(); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[42] ); - } - if((gTacticalStatus.Team[ 0 ].bTeamActive==1 || gTacticalStatus.Team[ 6 ].bTeamActive==1 || gTacticalStatus.Team[ 7 ].bTeamActive==1 || gTacticalStatus.Team[ 0 ].bTeamActive==1 || gTacticalStatus.Team[ 9 ].bTeamActive==1 )&& NumEnemyInSector() > 0)return(TRUE); - else return(FALSE); + teamwiped(); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[42] ); + } - - + if (PLAYER_BSIDE == 0) + { + // check game end for DeathMatch + return (numActiveSides > 1); + } + else if (PLAYER_BSIDE == 1) + { + // check game end for Team Deathmatch + // count how many active deathmatch teams are alive (two players could be alive but might be on the same TEAM) + for (int i=0; i < 4; i++) + { + if (dm_teams[i] > 0) + { + numActiveSides++; + } + } + + return (numActiveSides > 1); + } + else if (PLAYER_BSIDE == 2) + { + // check for game end for CO-OP + // If any player team is alive && the number of enemies > 0 then continue game (true), else quit (false) + return ((gTacticalStatus.Team[ 0 ].bTeamActive==1 || gTacticalStatus.Team[ 6 ].bTeamActive==1 || gTacticalStatus.Team[ 7 ].bTeamActive==1 || gTacticalStatus.Team[ 8 ].bTeamActive==1 || gTacticalStatus.Team[ 9 ].bTeamActive==1 )&& NumEnemyInSector() > 0); + } + + // dont stop the game by default + return true; } void UpdateSoldierToNetwork ( SOLDIERTYPE *pSoldier ) @@ -2554,6 +2967,174 @@ void recieve_door (RPCParameters *rpcParameters) } +void recieveCHATMSG(RPCParameters* rpcParameters) +{ + chat_msg* cmsg = (chat_msg*)rpcParameters->input; + + if (PLAYER_BSIDE==1 && cmsg->bToAll == false) + { + // If Team deathmatch && msg is allies only + if (client_teams[cmsg->client_num-1] == TEAM) + { + // only display on an ally client + ChatLogMessage( FONT_LTGREEN, MSG_CHAT, cmsg->msg); + } + } + else + { + // display to all clients + ChatLogMessage( FONT_LTGREEN, MSG_CHAT, cmsg->msg); + } +} + +/// OJW - 20081223 +// recieveDISCONNECT - Handle disconnection +void recieveDISCONNECT(RPCParameters* rpcParameters) +{ + // for starters - we shouldnt get a message for ourselves :) + int cl_num = (int) *rpcParameters->input; // cl_num starts at 1 + + wchar_t szPlayerName[30]; + memset(szPlayerName,0,30*sizeof(wchar_t)); + mbstowcs( szPlayerName,client_names[cl_num-1],30); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[47], szPlayerName ); + + // clear our records from this client + memset(client_names[cl_num-1],NULL,sizeof(char)*30); + memset(&client_ready[cl_num-1],0,sizeof(int)); + memset(&client_teams[cl_num-1],0,sizeof(int)); + + if (guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & INCOMBAT)) + { + // in the map screen and not in combat + // refresh player list to remove from the game + fDrawCharacterList = true; // set the character list to be redrawn + fTeamPanelDirty = true; // redraw the background + } + else if (guiCurrentScreen == GAME_SCREEN) // get a more valid check that the game is in progress here + { + // in tactical screen and in combat + // kill the dead clients mercs out of the game + + UINT8 iNetbTeam = (cl_num)+5; + UINT8 iubID_prefix = gTacticalStatus.Team[ iNetbTeam ].bFirstID;//over here now + + // kill any alive soldiers for the disconnected team + SOLDIERTYPE *pTeamSoldier; + INT32 cnt = 0; + + for ( pTeamSoldier = Menptr, cnt = 0; cnt < TOTAL_SOLDIERS; pTeamSoldier++, cnt++ ) + { + if ( pTeamSoldier->bActive && pTeamSoldier->bInSector && !( pTeamSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) ) + { + // Checkf for any more bacguys + if ( !pTeamSoldier->aiData.bNeutral && (pTeamSoldier->bTeam == iNetbTeam ) ) + { + // KIll...... + pTeamSoldier->SoldierTakeDamage( ANIM_CROUCH, pTeamSoldier->stats.bLife, 100, TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, TRUE ); + } + } + } + + // if it was that teams turn then end it + if(is_server) + { + if(gTacticalStatus.ubCurrentTeam==iNetbTeam)EndTurn( iNetbTeam+1 ); + } + } + +} + +void disconnected_callback(UINT8 ubResult) +{ + if (iDisconnectedScreen == MAP_SCREEN) + { + // clean up all resources and exit from map to main menu + RequestTriggerExitFromMapscreen(MAP_EXIT_TO_MAINMENU); + // game is restarted in HandleExitsFromMapScreen() + } + else if (iDisconnectedScreen == GAME_SCREEN) + { + // clean up all resources and exit from tactical to main menu + LeaveTacticalScreen(MAINMENU_SCREEN); + } + else if (iDisconnectedScreen == OPTIONS_SCREEN) + { + // Re-initialise the game + ReStartingGame(); + // clean up all resources and exit from options to main menu + SetOptionsExitScreen( MAINMENU_SCREEN ); + } + else if (iDisconnectedScreen == LAPTOP_SCREEN) + { + // Re-initialise the game + ReStartingGame(); + // clean up all resources and exit from laptop to main menu + SetPendingNewScreen(MAINMENU_SCREEN); // Laptop screen is always cleaned up on screen change in gameloop + } + else + { + // Re-initialise the game + ReStartingGame(); + // else dont clean "everything" but still exit to main menu + SetPendingNewScreen(MAINMENU_SCREEN); + } + +} + +// Gracefully handle self-disconnection of the client by Dropout +void HandleClientConnectionLost() +{ + if (guiCurrentScreen != MP_SCORE_SCREEN) + { + // cleanup client + client_disconnect(); + auto_retry = false; + + if (is_server) + server_disconnect(); + + // connection lost, let user know via popup then quit to main menu + iDisconnectedScreen = guiCurrentScreen; + SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; + + UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, (recieved_settings ? MPClientMessage[48] : MPClientMessage[55] ), guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect ); + + } + + /* + // handle UI + if (guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & INCOMBAT)) + { + // in the map screen and not in combat + + + // refresh player list to remove from the game + fDrawCharacterList = true; // set the character list to be redrawn + fTeamPanelDirty = true; // redraw the background + // can set connection retries here if desired + + } + else if (guiCurrentScreen == GAME_SCREEN) // get a more valid check that the game is in progress here + { + // connection lost, let user know via popup then quit to main menu + SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; + UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[48], MAINMENU_SCREEN, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ), NULL, &CenteringRect ); + + if (is_networked) + { + // haydent + if (is_server) + server_disconnect(); + + client_disconnect(); + + } + + //We want to reinitialize the game + ReStartingGame(); + }*/ +} void sendRT(void) { @@ -2614,6 +3195,16 @@ void teamwiped ( void ) client->RPC("sendWIPE",(const char*)&data, (int)sizeof(sc_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + if (is_server) + { + // end the co-op game if all player teams have wiped + if (PLAYER_BSIDE==MP_TYPE_COOP) + { + iTeamsWiped++; + if (iTeamsWiped >= MAX_CLIENTS) + game_over(); + } + } } void recieve_wipe (RPCParameters *rpcParameters) @@ -2621,10 +3212,18 @@ void recieve_wipe (RPCParameters *rpcParameters) { sc_struct* data = (sc_struct*)rpcParameters->input; ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, L"Team #%d is wiped out.", data->ubStartingTeam ); -if(is_server) -{ - if(gTacticalStatus.ubCurrentTeam==data->ubStartingTeam)EndTurn( data->ubStartingTeam+1 ); -} + if(is_server) + { + if(gTacticalStatus.ubCurrentTeam==data->ubStartingTeam)EndTurn( data->ubStartingTeam+1 ); + + // end the co-op game if all player teams have wiped + if (PLAYER_BSIDE==MP_TYPE_COOP) + { + iTeamsWiped++; + if (iTeamsWiped >= MAX_CLIENTS) + game_over(); + } + } } void send_heal (SOLDIERTYPE *pSoldier ) @@ -2681,6 +3280,44 @@ void awardINT (RPCParameters *rpcParameters) } +void game_over() +{ + // wait 3 seconds then notify all clients + is_game_over = true; + iScoreScreenTime = guiBaseJA2NoPauseClock + 5000; +} + +// OJW - note: i dont use the internal timer callback for this because +// death notices use it and i want all those to go through normally +// so all clients have time to receive death notice of the last merc +// which creates victory condition + +void send_gameover() +{ + // handle the user calling the wrong function first + if (!is_game_over) + { + game_over(); // start the event + return; + } + + // stop the event from firing again + is_game_over = false; + iScoreScreenTime = 0; + + // notify all the clients that the game is over + client->RPC("sendGAMEOVER",(const char*)&CLIENT_NUM, (int)sizeof(CLIENT_NUM)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +} + +void recieveGAMEOVER(RPCParameters *rpcParameters) +{ + player_stats* data= (player_stats*)rpcParameters->input; + memcpy(gMPPlayerStats,data,sizeof(player_stats)*5); + + // fire the score screen + StartScoreScreen(); +} + //*************************** //*** client connection***** @@ -2715,6 +3352,9 @@ void connect_client ( void ) REGISTER_STATIC_RPC(client, recieveREADY); REGISTER_STATIC_RPC(client, recieveGUI); REGISTER_STATIC_RPC(client, recieveSETTINGS); + REGISTER_STATIC_RPC(client, recieveTEAMCHANGE); + REGISTER_STATIC_RPC(client, recieveEDGECHANGE); + REGISTER_STATIC_RPC(client, recieveMAPCHANGE); REGISTER_STATIC_RPC(client, recieveBULLET); REGISTER_STATIC_RPC(client, recieveSTATE); REGISTER_STATIC_RPC(client, recieveDEATH); @@ -2730,6 +3370,9 @@ void connect_client ( void ) REGISTER_STATIC_RPC(client, recieve_wipe); REGISTER_STATIC_RPC(client, recieve_heal); REGISTER_STATIC_RPC(client, awardINT); + REGISTER_STATIC_RPC(client, recieveGAMEOVER); + REGISTER_STATIC_RPC(client, recieveDISCONNECT); + REGISTER_STATIC_RPC(client, recieveCHATMSG); //*** if (b) @@ -2763,9 +3406,18 @@ void connect_client ( void ) numready = 0; readystage = 0; status = 0; + is_game_started = false; gTacticalStatus.uiFlags&= (~SHOW_ALL_MERCS ); memset( &readyteamreg , 0 , sizeof (int) * 10); - + //OJW - 20081204 + memset ( &client_names,NULL,sizeof(int)*4); + memset ( &client_ready,0,sizeof(int)*4); + memset ( &client_teams,0,sizeof(int)*4); + if (!RANDOM_SPAWN) + memset ( &client_edges,0,sizeof(int)*4); + if (RANDOM_MERCS) + memset (random_mercs,0,sizeof(int)*7); + memset( gMPPlayerStats,0,sizeof(player_stats)*5); //retrieve settings from Ja2_mp.ini @@ -2880,6 +3532,10 @@ void connect_client ( void ) ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[1],SERVER_IP); client->Connect(SERVER_IP, atoi(SERVER_PORT), 0,0); is_connecting=true; + +#if _DEBUG + MPDebugMsg( String ( "connect_client()\n" ) ); +#endif } @@ -2903,79 +3559,118 @@ void client_packet ( void ) if (is_client) { - p = client->Receive(); + p = client->Receive(); - while(p) - { - //continue; // Didn't get any packets - - // We got a packet, get the identifier with our handy function - packetIdentifier = GetPacketIdentifier(p); - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"packet recieved"); - // Check if this is a network message packet - switch (packetIdentifier) + while(p) { - case ID_DISCONNECTION_NOTIFICATION: - // Connection lost normally - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_DISCONNECTION_NOTIFICATION"); - is_connected=false; - break; - case ID_ALREADY_CONNECTED: - // Connection lost normally - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_ALREADY_CONNECTED"); - break; - case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); - break; - case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_CONNECTION_LOST"); - break; - case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_NEW_INCOMING_CONNECTION"); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_ATTEMPT_FAILED"); - is_connected=false; - is_connecting=false; - break; - case ID_NO_FREE_INCOMING_CONNECTIONS: - // Sorry, the server is full. I don't do anything here but - // A real app should tell the user - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NO_FREE_INCOMING_CONNECTIONS"); - break; - case ID_CONNECTION_LOST: - // Couldn't deliver a reliable packet - i.e. the other system was abnormally - // terminated - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_LOST"); - is_connected=false; - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - // This tells the client they have connected - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_REQUEST_ACCEPTED"); - is_connected=true; - is_connecting=false; - requestSETTINGS(); - //request_settings();//ask server for game settings... - break; - case ID_NEW_INCOMING_CONNECTION: - //tells server client has connected - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NEW_INCOMING_CONNECTION"); - break; - case ID_MODIFIED_PACKET: - // Cheater! - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_MODIFIED_PACKET"); - break; - default: - - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"** a packet has been recieved for which i dont know what to do... **"); - break; + //continue; // Didn't get any packets + + // We got a packet, get the identifier with our handy function + packetIdentifier = GetPacketIdentifier(p); + //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"packet recieved"); + // Check if this is a network message packet + switch (packetIdentifier) + { + case ID_DISCONNECTION_NOTIFICATION: + // Connection lost normally + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_DISCONNECTION_NOTIFICATION"); + is_connected=false; + //OJW - 20081223 + //Gracefully notify and disconnect the client + client->DeallocatePacket(p); // HandleClientConnectionLost shuts down the client + HandleClientConnectionLost(); + //OJW - 2009 + return; + break; + case ID_ALREADY_CONNECTED: + // Connection lost normally + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_ALREADY_CONNECTED"); + break; + case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); + break; + case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_CONNECTION_LOST"); + break; + case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_NEW_INCOMING_CONNECTION"); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_ATTEMPT_FAILED"); + is_connected=false; + is_connecting=false; + + //OJW - 20081224 + // handle auto retry + if (auto_retry && giNumTries > 0) + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[49],giNumTries); // we already tried once, let the user know we are retrying + else + ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[50],giNumTries); // we already tried once, let the user know we are retrying + giNextRetryTime = guiBaseJA2NoPauseClock + 5000; // 5 seconds? + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + // Sorry, the server is full. I don't do anything here but + // A real app should tell the user + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NO_FREE_INCOMING_CONNECTIONS"); + break; + case ID_CONNECTION_LOST: + // Couldn't deliver a reliable packet - i.e. the other system was abnormally + // terminated + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_LOST"); + is_connected=false; + //OJW - 20081223 + //Gracefully notify and disconnect the client + client->DeallocatePacket(p); // HandleClientConnectionLost shuts down the client + HandleClientConnectionLost(); + return; + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + // This tells the client they have connected + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_REQUEST_ACCEPTED"); + is_connected=true; + is_connecting=false; + requestSETTINGS(); + //request_settings();//ask server for game settings... + break; + case ID_NEW_INCOMING_CONNECTION: + //tells server client has connected + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NEW_INCOMING_CONNECTION"); + break; + case ID_MODIFIED_PACKET: + // Cheater! + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_MODIFIED_PACKET"); + break; + default: + + ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"** a packet has been recieved for which i dont know what to do... **"); + break; + } + + + // We're done with the packet, get more :) + client->DeallocatePacket(p); + p = client->Receive(); } + // OJW - 20081223 + if (is_game_over) + { + if (guiBaseJA2NoPauseClock >= iScoreScreenTime) + { + send_gameover(); + } + } - // We're done with the packet, get more :) - client->DeallocatePacket(p); - p = client->Receive(); - } + // OJW - 20090203 + // Using the built in callback functions didnt work, so doing manually here + if (bClosingChatBoxToStartGame) + { + if (guiBaseJA2NoPauseClock >= iCCStartGameTime) + { + bClosingChatBoxToStartGame = false; + StartBattleChatBoxClosedCallback(); + } + } } } // Copied from Multiplayer.cpp @@ -3007,14 +3702,127 @@ void client_disconnect (void) allowlaptop=false; + // clear local client cache + memset(client_names,0,sizeof(char)*4*30); + memset(client_edges,0,sizeof(int)*4); + memset(client_ready,0,sizeof(int)*4); + memset(client_teams,0,sizeof(int)*4); + memset(gMPPlayerStats,0,sizeof(player_stats)*5); + memset(random_mercs,0,sizeof(int)*7); + + // We're done with the network RakNetworkFactory::DestroyRakPeerInterface(client); ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"client disconnected and shutdown"); + +#if _DEBUG + MPDebugMsg( "client_disconnect()\n" ); +#endif } else { ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"client is not running"); } } + +//OJW - 20081204 - send a starting edge change to all the clients +void send_edgechange(int newedge) +{ + // remove this godawful hack with a proper game status check + if (can_edgechange()) + { + edgechange_struct lan; + + lan.client_num = CLIENT_NUM; + lan.newedge = newedge; + + client->RPC("sendEDGECHANGE",(const char*)&lan, (int)sizeof(edgechange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + + // redraw the character list on the map screen + fDrawCharacterList = true; + fTeamPanelDirty = true; + client_edges[CLIENT_NUM-1] =newedge; + // store the setting locally + _itoa(newedge,SECT_EDGE,10); + + } + else + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + } +} + +bool can_edgechange() +{ + return (is_game_started != 1 && client_ready[CLIENT_NUM-1] == 0 && !allowlaptop && !RANDOM_SPAWN); +} + +//OJW - 20081204 - send a starting team change to all the clients +void send_teamchange(int newteam) +{ + // remove this godawful hack with a proper game status check + if (can_teamchange()) + { + teamchange_struct lan; + + lan.client_num = CLIENT_NUM; + lan.newteam = newteam; + + client->RPC("sendTEAMCHANGE",(const char*)&lan, (int)sizeof(teamchange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + + // redraw the character list on the map screen + fDrawCharacterList = true; + fTeamPanelDirty = true; + client_teams[lan.client_num-1] = lan.newteam; + TEAM = newteam; + + } + else + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + } +} + +bool can_teamchange() +{ + return (is_game_started != 1 && client_ready[CLIENT_NUM-1] == 0 && !allowlaptop); +} + +// 20081222 - OJW +void StartScoreScreen( void ) +{ + // pause game + + // set main screen as score screen + LeaveTacticalScreen( MP_SCORE_SCREEN ); +} + +void ChatCallback( UINT8 ubResult ) +{ + if (ubResult == MSG_BOX_RETURN_OK && wcscmp(gszChatBoxInputString,L"") > 0) + { + chat_msg cmsg; + wchar_t szPlayerName[30]; + memset(szPlayerName,0,30*sizeof(wchar_t)); + mbstowcs( szPlayerName,CLIENT_NAME,30); + + cmsg.bToAll = gbChatSendToAll; + if (PLAYER_BSIDE==1 && !cmsg.bToAll) + swprintf(cmsg.msg,MPClientMessage[56], szPlayerName, gszChatBoxInputString); + else + swprintf(cmsg.msg,MPClientMessage[52], szPlayerName, gszChatBoxInputString); + cmsg.client_num = CLIENT_NUM; + + // notify all of the chat message + client->RPC("sendCHATMSG",(const char*)&cmsg, (int)sizeof(chat_msg)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } + + memset(gszMsgBoxInputString,0,sizeof(gszMsgBoxInputString)); +} + +void OpenChatMsgBox( void ) +{ + DoChatBox((guiCurrentScreen == GAME_SCREEN? true : false),gzMPChatboxText[1],guiCurrentScreen,ChatCallback,NULL); +} \ No newline at end of file diff --git a/Multiplayer/connect.h b/Multiplayer/connect.h index f059ac6e..2f1e5a86 100644 --- a/Multiplayer/connect.h +++ b/Multiplayer/connect.h @@ -10,6 +10,7 @@ extern bool is_connecting; extern bool is_client; extern bool is_server; extern bool is_networked; +extern bool is_host; // OJW - added 20081129 extern int PLAYER_TEAM_TIMER_SEC_PER_TICKS; @@ -23,6 +24,7 @@ extern int CIV_ENABLED; extern int MAX_CLIENTS; extern int SAME_MERC; +extern int PLAYER_BSIDE; extern bool allowlaptop; @@ -30,6 +32,13 @@ extern UINT8 netbTeam; extern UINT8 ubID_prefix; extern FLOAT DAMAGE_MULTIPLIER; +//OJW - 20081218 +extern int RANDOM_SPAWN; +extern int RANDOM_MERCS; + +//OJW - 20090317 +extern bool is_game_started; + extern UINT16 crate_usMapPos; //extern int INTERRUPTS; @@ -60,6 +69,8 @@ void start_server (void); void client_packet ( void ); void server_packet ( void ); +void NetworkAutoStart(); // OJW - 20081130 - Automate connection functions + void server_disconnect (void); void client_disconnect (void); @@ -85,11 +96,53 @@ void send_stop (EV_S_STOP_MERC *SStopMerc); void send_interrupt(SOLDIERTYPE *pSoldier); +void OpenChatMsgBox(void); + BOOLEAN CheckConditionsForBattle( GROUP *pGroup ); // this comes from strategic movement.cpp extern char client_names[4][30]; +// OJW - 20081204 +// I need to keep track of all the clients readyness, spawn edge and team +// want to change this all to use client_info, but dont want to make sweeping +// changes to the codebase without talking to the other devs +extern int client_ready[4]; +extern int client_teams[4]; +extern int client_edges[4]; + +extern char SERVER_NAME[30]; + +//OJW - 20081224 +extern bool auto_retry; +extern int giNumTries; void send_settings(); +void send_mapchange(); // added 20081201 by OJW +void send_teamchange( int newteam ); // send team a change +void send_edgechange( int newedge ); // send an edge change +bool can_teamchange(); // UI Checks +bool can_edgechange(); // UI Checks + void StartInterrupt( void ); + +// MP GameTypes +enum +{ + MP_TYPE_DEATHMATCH, + MP_TYPE_TEAMDEATMATCH, + MP_TYPE_COOP, + NUM_MP_GAMETYPE +}; + +typedef struct +{ + int kills; + int deaths; + int hits; + int misses; +} player_stats; + +extern player_stats gMPPlayerStats[5]; + +extern void game_over( void ); diff --git a/Multiplayer/fresh_header.h b/Multiplayer/fresh_header.h index 87df7f1e..ecdbde14 100644 --- a/Multiplayer/fresh_header.h +++ b/Multiplayer/fresh_header.h @@ -10,6 +10,8 @@ typedef struct { UINT16 soldier_id; UINT16 attacker_id; + UINT8 attacker_team; + UINT8 soldier_team; }death_struct; typedef struct diff --git a/Multiplayer/network.h b/Multiplayer/network.h index abc22942..48aa3cb8 100644 --- a/Multiplayer/network.h +++ b/Multiplayer/network.h @@ -3,6 +3,7 @@ //this one just for structs, variables and functions used between the client and server scripts... extern char CLIENT_NAME[30]; +extern char SERVER_NAME[30]; extern bool Sawarded; @@ -41,6 +42,10 @@ typedef struct UINT8 client_num; char client_name[30]; char client_names[4][30]; + // OJW - added 20081204 + int client_edges[4]; + int client_teams[4]; + char server_name[30]; //int cl_ops[4]; int team; int TESTING; @@ -52,8 +57,34 @@ typedef struct int WEAPON_READIED_BONUS; int ALLOW_CUSTOM_NIV; int DISABLE_SPEC_MODE; + // OJW - added 20081220 + int RANDOM_SPAWN; + int RANDOM_MERCS; + int random_mercs[7]; } settings_struct; + + +// added 080101 by OJW +typedef struct +{ + INT16 gsMercArriveSectorX; + INT16 gsMercArriveSectorY; + float TIME; +} mapchange_struct; + +typedef struct +{ + UINT8 client_num; + UINT8 newedge; +} edgechange_struct; + +typedef struct +{ + UINT8 client_num; + UINT8 newteam; +} teamchange_struct; + //typedef struct //{ // int clnum; diff --git a/Multiplayer/server.cpp b/Multiplayer/server.cpp index 9cda3c0d..32bea4d3 100644 --- a/Multiplayer/server.cpp +++ b/Multiplayer/server.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "types.h" #include "gamesettings.h" @@ -26,11 +28,12 @@ int gsREPORT_NAME; #include "game init.h" #include "text.h" +#include "network.h" #include "connect.h" #include "message.h" -#include "network.h" #include "overhead.h" #include "fresh_header.h" +#include "Debug Control.h" UINT16 nc; //number of open connection UINT16 ncr; //number of ready confirmed connections //something keep record of ready connections .. @@ -74,6 +77,68 @@ typedef struct client_data client_d[4]; +// OJW - 20081223 +// Random Merc teams +// First attempt at "balanced" teams +int random_merc_teams[4][7] = +{ + { 16, 10, 19, 25, 4 , 11, 39 } , // Gus , Shadow, Spider , Raven , Vicki , Red , Meltdown + { 29, 36, 28, 2 , 22, 8 , 32 } , // Magic , Scope, Danny , Lynx , Hitman , Steroid , Malice + { 12, 5 , 20, 23, 48, 34, 17 } , // Reaper , Trevor, Cliff , Buzz , Cougar , Nails , Buns + { 31, 7 , 33, 35, 27, 37, 1 } // Scully , Ivan , Dr Q , Thor , Len , Wolf , Blood +}; + +int client_mercteam[4] = { 0 , 1 , 2 , 3 }; // random index of random_merc_teams per player + +bool inline can_joingame(); + +int f_rec_num(int mode, SystemAddress sender)//from client data +{ + int x; + client_data cl_record; + for ( x=0; x<4;x++) + { + cl_record = client_d[x]; + + if(mode==0)//find empty slot for new record + { + if(cl_record.address.binaryAddress==0) + return(x); + } + if(mode==1)//wipe clean all + { + client_d[x].address.binaryAddress=0; + client_d[x].address.port=0; + client_d[x].cl_number=0; + } + if(mode==2)//clear one record + { + if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) + { + client_d[x].address.port=0; + client_d[x].cl_number=0; + client_d[x].address.binaryAddress=0; + return(254); + } + + } + // OJW - 090212 - look up client number + if (mode == 3) + { + if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) + { + return (x); + } + } + } + if(mode == 0)//'no free slots' + { + ScreenMsg( FONT_RED, MSG_CHAT, L"Client Record Error, Restart Server, and Report Error." ); + return (255); + } + return(254); +} + // use UNASSIGNED_SYSTEM_ADDRESS instead of rpcParameters->sender to send it back to yourself (the sender) // there is very little in here dependant on the game engine and originally started out as an independant dedicated server .exe, and could if go ther again ... hayden. //********* RPC SECTION ************ @@ -100,6 +165,15 @@ void sendFIRE(RPCParameters *rpcParameters) void sendHIT(RPCParameters *rpcParameters) { + EV_S_WEAPONHIT* hit = (EV_S_WEAPONHIT*)rpcParameters->input; + + int team = MercPtrs[ hit->ubAttackerID ]->bTeam; + if (team == 1) team = 5; + else if (team >= 6) team -= 6; + else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + gMPPlayerStats[team].hits++; + server->RPC("recieveHIT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } @@ -157,18 +231,69 @@ void sendSTATE(RPCParameters *rpcParameters) } void sendDEATH(RPCParameters *rpcParameters) { + // the master copy of the scoreboard is kept on the server + death_struct* nDeath = (death_struct*)rpcParameters->input; + + // Save Stats on the server side + gMPPlayerStats[nDeath->soldier_team-1].deaths++; + gMPPlayerStats[nDeath->attacker_team-1].kills++; + + + // get the client number of the client sending the message + int iCLnum = f_rec_num(3,rpcParameters->sender)+1; + server->RPC("recieveDEATH",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); + +#if _DEBUG + wchar_t ateam[5]; + wchar_t steam[5]; + wchar_t clnum[5]; + _itow(nDeath->attacker_team,ateam,10); + _itow(nDeath->soldier_team,steam,10); + _itow(iCLnum,clnum,10); + ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"DEBUG: Soldier Killed : Attacking team %s , Soldier Team %s, Sender %s",ateam,steam,clnum); + char logmsg[100]; + sprintf(logmsg, "MP DEBUG: Soldier Killed #%i : Attacking team %i , Soldier Team %i, Sender %i\n",nDeath->soldier_id,nDeath->attacker_team,nDeath->soldier_team,iCLnum); + MPDebugMsg( logmsg ); +#endif } void sendhitSTRUCT(RPCParameters *rpcParameters) { + EV_S_STRUCTUREHIT* miss = (EV_S_STRUCTUREHIT*)rpcParameters->input; + + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + if (team == 1) team = 5; + else if (team >= 6) team -= 6; + else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + gMPPlayerStats[team].misses++; + server->RPC("recievehitSTRUCT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendhitWINDOW(RPCParameters *rpcParameters) { + EV_S_WINDOWHIT* miss = (EV_S_WINDOWHIT*)rpcParameters->input; + + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + if (team == 1) team = 5; + else if (team >= 6) team -= 6; + else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + gMPPlayerStats[team].misses++; + server->RPC("recievehitWINDOW",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendMISS(RPCParameters *rpcParameters) { + EV_S_MISS* miss = (EV_S_MISS*)rpcParameters->input; + + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + if (team == 1) team = 5; + else if (team >= 6) team -= 6; + else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + gMPPlayerStats[team].misses++; + server->RPC("recieveMISS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void updatenetworksoldier(RPCParameters *rpcParameters) @@ -205,6 +330,18 @@ void sendHEAL(RPCParameters *rpcParameters) { server->RPC("recieve_heal",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } + +// OJW - edge and team changes +void sendEDGECHANGE(RPCParameters *rpcParameters) +{ + server->RPC("recieveEDGECHANGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); +} + +void sendTEAMCHANGE(RPCParameters *rpcParameters) +{ + server->RPC("recieveTEAMCHANGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); +} + // //void rINT(RPCParameters *rpcParameters)//who is first //{ @@ -276,119 +413,218 @@ void sendREAL(RPCParameters *rpcParameters) } } -int f_rec_num(int mode, SystemAddress sender)//from client data + +// 20081222 - OJW +void sendGAMEOVER(RPCParameters *rpcParameters) { + // ignore the RPCParams and send the server side scoreboard + server->RPC("recieveGAMEOVER",(const char*)gMPPlayerStats, sizeof(gMPPlayerStats)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +} + +void sendCHATMSG(RPCParameters *rpcParameters) +{ + // ignore the RPCParams and send the server side scoreboard + server->RPC("recieveCHATMSG",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +} + + + + +// OJW - 20081223 +// fix client disconnecting mid game, allowing the game to proceed +void HandleDisconnect(SystemAddress sender) +{ + // find the CLIENT_NUM of the player int x; client_data cl_record; + for ( x=0; x<4;x++) { cl_record = client_d[x]; - - if(mode==0)//find empty slot for new record + if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) { - if(cl_record.address.binaryAddress==0) - return(x); + // notify all the clients of the disconnect + server->RPC("recieveDISCONNECT",(const char*)&cl_record.cl_number , sizeof(int)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + f_rec_num(2,sender); // remove from server's client list + break; } - if(mode==1)//wipe clean all - { - client_d[x].address.binaryAddress=0; - client_d[x].address.port=0; - client_d[x].cl_number=0; - } - if(mode==2)//clear one record - { - if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) - { - client_d[x].address.port=0; - client_d[x].cl_number=0; - client_d[x].address.binaryAddress=0; - return(254); - } - - } - - } - if(mode == 0)//'no free slots' - { - ScreenMsg( FONT_RED, MSG_CHAT, L"Client Record Error, Restart Server, and Report Error." ); - return (255); - } - return(254); } +// OJW - 20081218 +// shuffle an integer array +// remove shuffledList and put directly into arr[] +void rSortArray(int* arr, int len) +{ + std::list tmpList; + std::list shuffledList; + std::list::iterator Iter; + int i=0; + + // add all our array items + for(i=0; i0; iRandPos--, Iter++); + //Iter += iRandPos; + shuffledList.push_front(*Iter); + tmpList.erase(Iter); + } + + // add all our elements + for(i=0; iinput; - client_info* clinf = (client_info*)rpcParameters->input; + //server assigned client numbers - hayden. + SystemAddress sender = rpcParameters->sender;//get senders address + int bslot = f_rec_num(0,blank);//get empty record slot + client_d[bslot].address=sender; //record clients address + int new_cl_num = bslot+1;//client number to assign + client_d[bslot].cl_number=new_cl_num; //record clients number - //server assigned client numbers - hayden. - SystemAddress sender = rpcParameters->sender;//get senders address - int bslot = f_rec_num(0,blank);//get empty record slot - client_d[bslot].address=sender; //record clients address - int new_cl_num = bslot+1;//client number to assign - client_d[bslot].cl_number=new_cl_num; //record clients number - - settings_struct lan; + settings_struct lan; - //lan.client_num = clinf->client_num;//old client assigned number - lan.client_num = new_cl_num; //new server assigned number - strcpy(lan.client_name , clinf->client_name); + //lan.client_num = clinf->client_num;//old client assigned number + lan.client_num = new_cl_num; //new server assigned number + strcpy(lan.client_name , clinf->client_name); - lan.max_clients = gsMAX_CLIENTS; - memcpy(lan.kitbag , kbag,sizeof (char)*100); - lan.damage_multiplier = gsDAMAGE_MULTIPLIER; + // OJW - 20081204 + strcpy(lan.server_name , SERVER_NAME); + memcpy(lan.client_edges,client_edges,sizeof(int)*4); + memcpy(lan.client_teams,client_teams,sizeof(int)*4); + + lan.RANDOM_SPAWN = RANDOM_SPAWN; + lan.RANDOM_MERCS = RANDOM_MERCS; + + lan.max_clients = gsMAX_CLIENTS; + memcpy(lan.kitbag , kbag,sizeof (char)*100); + lan.damage_multiplier = gsDAMAGE_MULTIPLIER; + + lan.same_merc = gsSAME_MERC; + lan.gsMercArriveSectorX=gsMercArriveSectorX; + lan.gsMercArriveSectorY=gsMercArriveSectorY; + + lan.ENEMY_ENABLED=ENEMY_ENABLED; + lan.CREATURE_ENABLED=CREATURE_ENABLED; + lan.MILITIA_ENABLED=MILITIA_ENABLED; + lan.CIV_ENABLED=CIV_ENABLED; + + lan.gsPLAYER_BSIDE=gsPLAYER_BSIDE; + + lan.emorale=gsMORALE; + lan.gsREPORT_NAME=gsREPORT_NAME; + //something new + lan.secs_per_tick=gssecs_per_tick; + lan.soubBobbyRay=gGameOptions.ubBobbyRay; + lan.sofGunNut=gGameOptions.fGunNut; + lan.soubGameStyle=gGameOptions.ubGameStyle; + lan.soubDifficultyLevel=gGameOptions.ubDifficultyLevel; + lan.sofTurnTimeLimit=gGameOptions.fTurnTimeLimit; + lan.sofIronManMode=gGameOptions.fIronManMode; + lan.starting_balance=gsstarting_balance; - lan.same_merc = gsSAME_MERC; + // OJW - 20090319 - Changing allow custom NIV to force NIV on all clients + // as with the new join screen setup, no way for the individual client to choose + // whether or not to use NIV. If this is wrong, its easily fixed... + // just change description text for the toggle option on the host screen to + // "Allow custom inventory" and delete this if statement. + if (sALLOW_CUSTOM_NIV==1) + gGameOptions.ubInventorySystem = INVENTORY_NEW; + + lan.sofNewInv=gGameOptions.ubInventorySystem; + + lan.soDis_Bobby=gsDis_Bobby; + lan.soDis_Equip=gsDis_Equip; + + lan.gsMAX_MERCS=gsMAX_MERCS; + + memcpy( lan.client_names , client_names, sizeof( char ) * 4 * 30 ); + lan.team=clinf->team; + + lan.TESTING=gsTESTING; + + // OJW - 20081218 + if (RANDOM_SPAWN) + lan.cl_edge = client_edges[lan.client_num-1]; + else + lan.cl_edge=clinf->cl_edge; + + // OJW - 20081223 + if (RANDOM_MERCS) + { + memcpy(lan.random_mercs, random_merc_teams[client_mercteam[lan.client_num - 1]], sizeof(int) * 7); + } + + lan.TIME=TIME; + lan.WEAPON_READIED_BONUS=sWEAPON_READIED_BONUS; + lan.ALLOW_CUSTOM_NIV=sALLOW_CUSTOM_NIV; + lan.DISABLE_SPEC_MODE=sDISABLE_SPEC_MODE; + + + server->RPC("recieveSETTINGS",(const char*)&lan, (int)sizeof(settings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } // end if(can_joingame) +} + +// added 081201 by Owen , allow the server to change the map while its still not in laptop mode +void send_mapchange(void) +{ + if (is_server && !allowlaptop) + { + mapchange_struct lan; + lan.gsMercArriveSectorX=gsMercArriveSectorX; lan.gsMercArriveSectorY=gsMercArriveSectorY; + lan.TIME=TIME; - lan.ENEMY_ENABLED=ENEMY_ENABLED; - lan.CREATURE_ENABLED=CREATURE_ENABLED; - lan.MILITIA_ENABLED=MILITIA_ENABLED; - lan.CIV_ENABLED=CIV_ENABLED; + server->RPC("recieveMAPCHANGE",(const char*)&lan, (int)sizeof(mapchange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } + else + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[45]); + } +} - lan.gsPLAYER_BSIDE=gsPLAYER_BSIDE; - - lan.emorale=gsMORALE; - lan.gsREPORT_NAME=gsREPORT_NAME; -//something new - lan.secs_per_tick=gssecs_per_tick; - lan.soubBobbyRay=gGameOptions.ubBobbyRay; - lan.sofGunNut=gGameOptions.fGunNut; - lan.soubGameStyle=gGameOptions.ubGameStyle; - lan.soubDifficultyLevel=gGameOptions.ubDifficultyLevel; - lan.sofTurnTimeLimit=gGameOptions.fTurnTimeLimit; - lan.sofIronManMode=gGameOptions.fIronManMode; - lan.starting_balance=gsstarting_balance; - - lan.sofNewInv=gGameOptions.ubInventorySystem; - - lan.soDis_Bobby=gsDis_Bobby; - lan.soDis_Equip=gsDis_Equip; - - lan.gsMAX_MERCS=gsMAX_MERCS; - - memcpy( lan.client_names , client_names, sizeof( char ) * 4 * 30 ); - lan.team=clinf->team; - - - lan.TESTING=gsTESTING; - - lan.cl_edge=clinf->cl_edge; - lan.TIME=TIME; - lan.WEAPON_READIED_BONUS=sWEAPON_READIED_BONUS; - lan.ALLOW_CUSTOM_NIV=sALLOW_CUSTOM_NIV; - lan.DISABLE_SPEC_MODE=sDISABLE_SPEC_MODE; - - - server->RPC("recieveSETTINGS",(const char*)&lan, (int)sizeof(settings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +// OJW 090212 +bool inline can_joingame() +{ + return !allowlaptop; +} +// Allow server to disconnect incoming clients after the game has started +void CheckIncomingConnection(Packet* p) +{ + // some clients might reconnect after disconnecting, either after the laptop is unlocked + // or after the game has started + // we dont want to allow this as thier game will be out of state + if (!can_joingame()) + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"CONNECTION REJECTED - GAME HAS STARTED"); + // disconnect this client, no need to notify them as they will know if they disconnected + // before receiving a settings packet that they were not allowed to join + server->CloseConnection(p->systemAddress, true); + } } @@ -428,10 +664,18 @@ void start_server (void) char time_div[30]; char mor[30]; + char sRandomMercs[30]; + char sRandomEdges[30]; + + // OJW - 20081204 + GetPrivateProfileString( "Ja2_mp Settings","SERVER_NAME", "", SERVER_NAME, MAX_PATH, "..\\Ja2_mp.ini" ); + GetPrivateProfileString( "Ja2_mp Settings","SAME_MERC", "", hire_same_merc, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","DISABLE_MORALE", "", mor, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","MAX_CLIENTS", "", maxclients, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","DAMAGE_MULTIPLIER", "", net_div, MAX_PATH, "..\\Ja2_mp.ini" ); + GetPrivateProfileString( "Ja2_mp Settings","RANDOM_MERCS", "", sRandomMercs, MAX_PATH, "..\\Ja2_mp.ini" ); + GetPrivateProfileString( "Ja2_mp Settings","RANDOM_EDGES", "", sRandomEdges, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","ENEMY_ENABLED", "", bteam1_enabled, MAX_PATH, "..\\Ja2_mp.ini" ); //GetPrivateProfileString( "Ja2_mp Settings","CREATURE_ENABLED", "", bteam2_enabled, MAX_PATH, "..\\Ja2_mp.ini" ); @@ -479,14 +723,30 @@ void start_server (void) MILITIA_ENABLED=0; CIV_ENABLED=0; -if(gsPLAYER_BSIDE==2)//only enable ai during coop -{ - ENEMY_ENABLED =atoi(bteam1_enabled); - //CREATURE_ENABLED =atoi(bteam2_enabled); - MILITIA_ENABLED =atoi(bteam3_enabled); - CIV_ENABLED =atoi(bteam4_enabled); + RANDOM_MERCS = atoi(sRandomMercs); + RANDOM_SPAWN = atoi(sRandomEdges); -} + if (RANDOM_SPAWN) + { + // create random starting edges + int spawns[4] = { 0 , 1 , 2 , 3 }; + rSortArray(spawns,4); + memcpy(client_edges,spawns,sizeof(int)*4); + } + + if (RANDOM_MERCS) + { + // randomly sort team indexes to give client + // one of four random merc teams + rSortArray(client_mercteam,4); + } + + if(gsPLAYER_BSIDE==2)//only enable ai during coop + { + ENEMY_ENABLED = 1; // always enable enemies in co-op + MILITIA_ENABLED = atoi(bteam3_enabled); + CIV_ENABLED = atoi(bteam4_enabled); + } gsSAME_MERC = atoi(hire_same_merc); gsDAMAGE_MULTIPLIER =(FLOAT)atof(net_div); @@ -560,6 +820,10 @@ if(gsPLAYER_BSIDE==2)//only enable ai during coop REGISTER_STATIC_RPC(server, startCOMBAT); REGISTER_STATIC_RPC(server, sendWIPE); REGISTER_STATIC_RPC(server, sendHEAL); + REGISTER_STATIC_RPC(server, sendEDGECHANGE); + REGISTER_STATIC_RPC(server, sendTEAMCHANGE); + REGISTER_STATIC_RPC(server, sendGAMEOVER); + REGISTER_STATIC_RPC(server, sendCHATMSG); //REGISTER_STATIC_RPC(server, rINT); // @@ -614,7 +878,7 @@ void server_packet ( void ) case ID_DISCONNECTION_NOTIFICATION://client disconnected purposefullly // Connection lost normally ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_DISCONNECTION_NOTIFICATION"); - f_rec_num(2, p->systemAddress);//clear record + HandleDisconnect(p->systemAddress);//clear record break; case ID_ALREADY_CONNECTED: // Connection lost normally @@ -641,7 +905,7 @@ void server_packet ( void ) // Couldn't deliver a reliable packet - i.e. the other system was abnormally // terminated ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_CONNECTION_LOST");//client dropped - f_rec_num(2, p->systemAddress);//clear record + HandleDisconnect(p->systemAddress);//clear record break; case ID_CONNECTION_REQUEST_ACCEPTED: // This tells the client they have connected @@ -650,6 +914,8 @@ void server_packet ( void ) case ID_NEW_INCOMING_CONNECTION: //tells server client has connected ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_NEW_INCOMING_CONNECTION"); + // make sure they can connect + CheckIncomingConnection(p); //send_settings();//send off server set settings break; case ID_MODIFIED_PACKET: @@ -700,4 +966,4 @@ void server_disconnect (void) { ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[7]); } -} +} \ No newline at end of file diff --git a/Options Screen.cpp b/Options Screen.cpp index 172a7e5b..af84da1f 100644 --- a/Options Screen.cpp +++ b/Options Screen.cpp @@ -253,7 +253,6 @@ void RenderOptionsScreen(); void ExitOptionsScreen(); void HandleOptionsScreen(); void GetOptionsScreenUserInput(); -void SetOptionsExitScreen( UINT32 uiExitScreen ); void SoundFXSliderChangeCallBack( INT32 iNewValue ); diff --git a/Options Screen.h b/Options Screen.h index 46872b8e..e4f3194a 100644 --- a/Options Screen.h +++ b/Options Screen.h @@ -23,6 +23,8 @@ UINT32 OptionsScreenInit( void ); void SetOptionsScreenToggleBoxes(); void GetOptionsScreenToggleBoxes(); +// OJW - moved this here so can exit options screen on client disconnect +void SetOptionsExitScreen( UINT32 uiExitScreen ); BOOLEAN DoOptionsMessageBox( UINT8 ubStyle, STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ); diff --git a/SCREENS.cpp b/SCREENS.cpp index 7387c435..46b2745e 100644 --- a/SCREENS.cpp +++ b/SCREENS.cpp @@ -55,7 +55,10 @@ Screens GameScreens[MAX_SCREENS] = { DemoExitScreenInit, DemoExitScreenHandle, DemoExitScreenShutdown }, { IntroScreenInit, IntroScreenHandle, IntroScreenShutdown }, { CreditScreenInit, CreditScreenHandle, CreditScreenShutdown }, - + { MPJoinScreenInit, MPJoinScreenHandle, MPJoinScreenShutdown }, // OJW - 20081129 + { MPHostScreenInit, MPHostScreenHandle, MPHostScreenShutdown }, + { MPScoreScreenInit, MPScoreScreenHandle, MPScoreScreenShutdown }, // OJW - 20081222 + { MPChatScreenInit, MPChatScreenHandle, MPChatScreenShutdown }, // OJW - 20090314 #ifdef JA2BETAVERSION { AIViewerScreenInit, AIViewerScreenHandle, AIViewerScreenShutdown }, diff --git a/Standard Gaming Platform/Button System.cpp b/Standard Gaming Platform/Button System.cpp index f1adb7a7..aad234aa 100644 --- a/Standard Gaming Platform/Button System.cpp +++ b/Standard Gaming Platform/Button System.cpp @@ -2467,6 +2467,43 @@ BOOLEAN SpecifyButtonIcon( INT32 iButtonID, INT32 iVideoObjectID, UINT16 usVideo return TRUE; } +//OJW - 20081224 +BOOLEAN SpecifyButtonImage( INT32 iButtonID, UINT32 iButtonImageID) +{ + GUI_BUTTON *b; + + Assert( iButtonID >= 0 ); + Assert( iButtonID < MAX_BUTTONS ); + b = ButtonList[ iButtonID ]; + Assert( b ); + + // Is there a QuickButton image in the given image slot? + if(ButtonPictures[iButtonImageID].vobj == NULL) + { + DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Invalid button image number"); + return false; + } + + b->ImageNum = iButtonImageID; + + return TRUE; +} + +BOOLEAN SpecifyButtonImage( GUI_BUTTON *b, UINT32 iButtonImageID) +{ + Assert( b ); + + // Is there a QuickButton image in the given image slot? + if(ButtonPictures[iButtonImageID].vobj == NULL) + { + DbgMessage(TOPIC_BUTTON_HANDLER,DBG_LEVEL_0,"QuickCreateButton: Invalid button image number"); + return false; + } + + b->ImageNum = iButtonImageID; + + return TRUE; +} void RemoveTextFromButton( INT32 iButtonID ) { GUI_BUTTON *b; diff --git a/Standard Gaming Platform/Button System.h b/Standard Gaming Platform/Button System.h index fd00a0d8..1970b131 100644 --- a/Standard Gaming Platform/Button System.h +++ b/Standard Gaming Platform/Button System.h @@ -287,6 +287,9 @@ void SpecifyButtonTextSubOffsets( INT32 iButtonID, INT8 bTextXOffset, INT8 bText void SpecifyButtonTextWrappedWidth(INT32 iButtonID, INT16 sWrappedWidth); void SpecifyButtonSoundScheme( INT32 iButtonID, INT8 bSoundScheme ); +//OJW - 20081224 +BOOLEAN SpecifyButtonImage( INT32 iButtonID, UINT32 iButtonImageID); +BOOLEAN SpecifyButtonImage( GUI_BUTTON *b, UINT32 iButtonImageID); void PlayButtonSound( INT32 iButtonID, INT32 iSoundType ); void AllowDisabledButtonFastHelp( INT32 iButtonID, BOOLEAN fAllow ); diff --git a/Strategic/Map Screen Interface Bottom.cpp b/Strategic/Map Screen Interface Bottom.cpp index f322f90d..410b5b65 100644 --- a/Strategic/Map Screen Interface Bottom.cpp +++ b/Strategic/Map Screen Interface Bottom.cpp @@ -49,6 +49,7 @@ #include "GameSettings.h" #include "_Ja25EnglishText.h" #include "SaveLoadScreen.h" +#include "game init.h" #endif #include "connect.h" @@ -1649,7 +1650,7 @@ BOOLEAN AnyUsableRealMercenariesOnTeam( void ) void RequestTriggerExitFromMapscreen( INT8 bExitToWhere ) { - Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_SAVE ) ); + Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_MAINMENU ) ); // if allowed to do so if ( AllowedToExitFromMapscreenTo( bExitToWhere ) ) @@ -1665,6 +1666,18 @@ void RequestTriggerExitFromMapscreen( INT8 bExitToWhere ) return; } } + else if (!(bExitToWhere == MAP_EXIT_TO_TACTICAL || bExitToWhere == MAP_EXIT_TO_MAINMENU)) + { + // OJW - 20090301 + if (is_networked && is_client) + { + if (client_ready[CLIENT_NUM-1]==1 && is_game_started == false) + { + // un-ready if we are not on map screen + start_battle(); + } + } + } // permit it, and get the ball rolling gbExitingMapScreenToWhere = bExitToWhere; @@ -1680,7 +1693,7 @@ void RequestTriggerExitFromMapscreen( INT8 bExitToWhere ) BOOLEAN AllowedToExitFromMapscreenTo( INT8 bExitToWhere ) { - Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_SAVE ) ); + Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_MAINMENU ) ); // if already leaving, disallow any other attempts to exit if ( fLeavingMapScreen ) @@ -1688,6 +1701,13 @@ BOOLEAN AllowedToExitFromMapscreenTo( INT8 bExitToWhere ) return( FALSE ); } + // OJW - 20090210 - clean resources on disconnect + if (bExitToWhere == MAP_EXIT_TO_MAINMENU) + { + // always allow this + return( TRUE ); + } + // WANNE: At least one merc must be hired so we can go to tactical. // This was an exploit. If you go to tactical in Omerta and have not hired any merc // then go back to laptop and hire a merc. After the merc arrives the heli landing was not @@ -1839,7 +1859,12 @@ void HandleExitsFromMapScreen( void ) guiPreviousOptionScreen = guiCurrentScreen; SetPendingNewScreen( SAVE_LOAD_SCREEN ); break; - + // OJW - 20090210 - clean resources on disconnect + case MAP_EXIT_TO_MAINMENU: + // Re-initialise the game + ReStartingGame(); + SetPendingNewScreen( MAINMENU_SCREEN ); + break; default: // invalid exit type Assert( FALSE ); diff --git a/Strategic/Map Screen Interface Bottom.h b/Strategic/Map Screen Interface Bottom.h index 25bb2820..54950ea6 100644 --- a/Strategic/Map Screen Interface Bottom.h +++ b/Strategic/Map Screen Interface Bottom.h @@ -15,6 +15,8 @@ enum{ MAP_EXIT_TO_OPTIONS, MAP_EXIT_TO_LOAD, MAP_EXIT_TO_SAVE, + // OJW - 20090210 - clean resources on disconnect + MAP_EXIT_TO_MAINMENU }; // there's no button for entering SAVE/LOAD screen directly... diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index dad46dff..a619022f 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -341,6 +341,37 @@ int SOLDIER_HAND_Y; //int CLOCK_X; //int CLOCK_Y; +// OJW: MP POSITIONS +int MP_BTN_Y; +int MP_ROWSTART_Y; +int MP_PLAYER_X; +int MP_PLAYER_W; +int MP_TEAM_X; +int MP_TEAM_W; +int MP_COMPASS_X; +int MP_COMPASS_W; +int MP_GAMEINFO_X; +int MP_GAMEINFO_W; + +#define MAX_MP_BUTTONS 4 + +INT gMapMPButtonsX[ MAX_MP_BUTTONS ] = {5 , 84 , 130 , 154 }; + +#define MP_BTN_STARTGAME 4 +#define MP_BTN_READY 3 + + +INT32 giMapMPButtonImage[ MAX_MP_BUTTONS+1 ] = { -1, -1, -1, -1 ,-1}; +INT32 giMapMPButton[ MAX_MP_BUTTONS ] = { -1, -1, -1, -1}; + +// Compass Drowndown +INT32 ghMPCompassBox; +INT32 ghMPTeamBox; +MOUSE_REGION gMPReadyButtonRegion; +MOUSE_REGION gCompassMenuRegion[MAX_EDGES]; +MOUSE_REGION gTeamMenuRegion[MAX_MP_TEAMS]; +bool is_compass_box_open = false; +bool is_team_box_open = false; #define RGB_WHITE ( FROMRGB( 255, 255, 255 ) ) #define RGB_YELLOW ( FROMRGB( 255, 255, 0 ) ) @@ -825,6 +856,23 @@ void AddTeamPanelSortButtonsForMapScreen( void ); void RemoveTeamPanelSortButtonsForMapScreen( void ); void SortListOfMercsInTeamPanel( BOOLEAN fRetainSelectedMercs, BOOLEAN fReverse = FALSE ); +// OJW - buttons and boxes for MP interface +void AddMPButtonsForMapScreen( void ); +void RemoveMPButtonsForMapScreen( void ); +void InitializeMPCoordinates( void ); +void CreateMPCompassBox( void ); +void DestroyMPCompassBox( void ); +void CreateMPTeamBox( void ); +void DestroyMPTeamBox( void ); +void MPReadyButtonCallback( GUI_BUTTON *btn, INT32 reason ); +void MPCompassChangeCallback( MOUSE_REGION * pReason, INT32 iReason); +void CreateDestroyMouseRegionsForCompassBox(); +void CompassBoxMvtCallback ( MOUSE_REGION* pRegion, INT32 iReason); +void CompassBoxBtnCallback ( MOUSE_REGION* pRegion, INT32 iReason); +void MPTeamChangeCallback( MOUSE_REGION * pReason, INT32 iReason); +void CreateDestroyMouseRegionsForTeamBox(); +void TeamBoxMvtCallback ( MOUSE_REGION* pRegion, INT32 iReason); +void TeamBoxBtnCallback ( MOUSE_REGION* pRegion, INT32 iReason); // Rendering void RenderCharacterInfoBackground( void ); @@ -977,6 +1025,7 @@ void BltCharInvPanel(); void DrawCharacterInfo(INT16 sCharNumber); void DisplayCharacterInfo( void ); void UpDateStatusOfContractBox( void ); +void DrawMPPlayerList (); // OJW - 20081201 // get which index in the mapscreen character list is this guy INT32 GetIndexForthis( SOLDIERTYPE *pSoldier ); @@ -1347,6 +1396,8 @@ BOOLEAN InitializeInvPanelCoordsOld() //CLOCK_X = (SCREEN_WIDTH - 86); //CLOCK_Y = (SCREEN_HEIGHT - 21); + //OJW - MP interface changes + InitializeMPCoordinates(); gSCamoXY.sX = INV_BODY_X; gSCamoXY.sY = INV_BODY_Y; // X, Y Location of Map screen's Camouflage region return ( TRUE ); @@ -1667,8 +1718,40 @@ BOOLEAN InitializeInvPanelCoordsNew() gSCamoXY.sX = INV_BODY_X; gSCamoXY.sY = INV_BODY_Y; // X, Y Location of Map screen's Camouflage region + //OJW - MP interface changes + InitializeMPCoordinates(); + + return ( TRUE ); } +void InitializeMPCoordinates() +{ + //OJW - MP interface changes + if (iResolution == 0) + { + MP_BTN_Y = 268; + } + else if (iResolution == 1) + { + MP_BTN_Y = 386; + } + else if (iResolution == 2) + { + MP_BTN_Y = 554; + } + + MP_ROWSTART_Y = MP_BTN_Y + 21; + MP_PLAYER_X = 5; + MP_PLAYER_W = 75; + MP_TEAM_X = 84; + MP_TEAM_W = 40; + MP_COMPASS_X = 130; + MP_COMPASS_W = 19; + MP_GAMEINFO_X = 154; + MP_GAMEINFO_W = 81; + + // End of MP interface changes +} BOOLEAN InitializeInvPanelCoordsVehicle( ) { InitializeInvPanelCoordsNew(); @@ -3666,6 +3749,9 @@ void DisplayCharacterList() } } + // draw multiplayer player list + if (is_networked && is_client) + DrawMPPlayerList(); HandleDisplayOfSelectedMercArrows( ); SetFontDestBuffer(FRAME_BUFFER ,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,FALSE); @@ -3677,6 +3763,487 @@ void DisplayCharacterList() return; } +// OJW - Added 081201 +// Draw Lan clients in the vehicle slots +// change the graphics, add more relevant info... +// this is just a temp test +void DrawMPPlayerList () +{ + INT16 usX=0; + INT16 usY=0; + + if (is_networked && is_client && is_connected) + { + wchar_t szPlayerName[30]; + wchar_t szTeam[20]; + wchar_t szCompass[2]; + int row=0; + for(int i=0; i < 4; i++) + { + //if (i != CLIENT_NUM) + //{ + if (client_names[i] != NULL) + { + if (client_ready[i]==1) + { + SetFontForeground( FONT_GREEN ); + } + else + { + SetFontForeground( FONT_WHITE ); + } + + if (strcmp(client_names[i],"")!=0) + { + // valid player + memset(szPlayerName,0,30*sizeof(wchar_t)); + mbstowcs( szPlayerName,client_names[i],30); + FindFontCenterCoordinates((short)MP_PLAYER_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_PLAYER_W, (short)Y_SIZE, szPlayerName, (long)MAP_SCREEN_FONT, &usX, &usY); + DrawString( (STR16)szPlayerName , usX,usY, MAP_SCREEN_FONT); + + // check for gametype here + //wcscpy(szTeam,L"N/A"); + if (PLAYER_BSIDE==MP_TYPE_DEATHMATCH) + wcscpy(szTeam,L"N/A"); + else + wcscpy(szTeam,gszMPTeamNames[client_teams[i]]); + FindFontCenterCoordinates((short)MP_TEAM_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_TEAM_W, (short)Y_SIZE, szTeam, (long)MAP_SCREEN_FONT, &usX, &usY); + DrawString( szTeam,usX,(short)usY, MAP_SCREEN_FONT); + + wcscpy(szCompass,(RANDOM_SPAWN==1 ? L"?" : gszMPEdgesText[client_edges[i]])); + FindFontCenterCoordinates((short)MP_COMPASS_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_COMPASS_W, (short)Y_SIZE, szCompass, (long)MAP_SCREEN_FONT, &usX, &usY); + DrawString(szCompass , usX,(short)usY, MAP_SCREEN_FONT); + + row++; + } + + } + + //} + } + + // Draw the Server Info + + // Server name + // change this text to wrap + /*wchar_t szServerName[30]; + memset(szServerName,0,30*sizeof(wchar_t)); + mbstowcs( szServerName,SERVER_NAME,30); + + SetFontForeground( FONT_YELLOW ); + DrawString( L"Server Name:" , MP_GAMEINFO_X ,MP_ROWSTART_Y, MAP_SCREEN_FONT); + SetFontForeground( FONT_WHITE ); + DrawString( (STR16)szServerName , MP_GAMEINFO_X ,MP_ROWSTART_Y+(1*Y_SIZE), MAP_SCREEN_FONT);*/ + + // Game Type + SetFontForeground( FONT_YELLOW ); + DrawString( gszMPMapscreenText[0] , MP_GAMEINFO_X ,MP_ROWSTART_Y, MAP_SCREEN_FONT); + SetFontForeground( FONT_WHITE ); + + switch(PLAYER_BSIDE) + { + case MP_TYPE_DEATHMATCH: + DrawString( gzMPHScreenText[MPH_DEATHMATCH_TEXT] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(1*Y_SIZE), MAP_SCREEN_FONT); + break; + case MP_TYPE_TEAMDEATMATCH: + DrawString( gzMPHScreenText[MPH_TEAMDM_TEXT] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(1*Y_SIZE), MAP_SCREEN_FONT); + break; + case MP_TYPE_COOP: + DrawString( gzMPHScreenText[MPH_COOP_TEXT] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(1*Y_SIZE), MAP_SCREEN_FONT); + break; + } + + // Max Players + SetFontForeground( FONT_YELLOW ); + DrawString( gszMPMapscreenText[1] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(2*Y_SIZE), MAP_SCREEN_FONT); + SetFontForeground( FONT_WHITE ); + + wchar_t szMaxPlayers[10]; + swprintf(szMaxPlayers,L"%i",MAX_CLIENTS); + DrawString( szMaxPlayers , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[1],MAP_SCREEN_FONT),MP_ROWSTART_Y+(2*Y_SIZE), MAP_SCREEN_FONT); + + // Number of Mercs + SetFontForeground( FONT_YELLOW ); + DrawString( gszMPMapscreenText[2] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(3*Y_SIZE), MAP_SCREEN_FONT); + SetFontForeground( FONT_WHITE ); + + wchar_t szSquadSize[10]; + swprintf(szSquadSize,L"%i",MAX_MERCS); + DrawString( szSquadSize , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[2],MAP_SCREEN_FONT),MP_ROWSTART_Y+(3*Y_SIZE), MAP_SCREEN_FONT); + + if (RANDOM_MERCS) + { + // Random Mercs + SetFontForeground( FONT_YELLOW ); + DrawString( gszMPMapscreenText[5] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(4*Y_SIZE), MAP_SCREEN_FONT); + SetFontForeground( FONT_WHITE ); + DrawString( gszMPMapscreenText[6] , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[5],MAP_SCREEN_FONT),MP_ROWSTART_Y+(4*Y_SIZE), MAP_SCREEN_FONT); + } + + } +} +void DestroyMPCompassBox( void ) +{ + RemoveBox(ghMPCompassBox); + ghMPCompassBox = -1; +} +void CreateMPCompassBox( void ) +{ + UINT32 hStringHandle; + UINT32 uiCounter; + + + // will create attribute pop up menu for mapscreen assignments + SGPPoint CompassPosition = { MP_COMPASS_X, MP_ROWSTART_Y }; + SGPRect CompassDimensions = { 0,0, 100, 95}; + + + // create basic box + CreatePopUpBox(&ghMPCompassBox, CompassDimensions, CompassPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); + + // which buffer will box render to + SetBoxBuffer(ghMPCompassBox, FRAME_BUFFER); + + // border type? + SetBorderType(ghMPCompassBox,guiPOPUPBORDERS); + + // background texture + SetBackGroundSurface(ghMPCompassBox, guiPOPUPTEX); + + // margin sizes + SetMargins(ghMPCompassBox, 6, 6, 4, 4 ); + + // space between lines + SetLineSpace(ghMPCompassBox, 2); + + // set current box to this one + SetCurrentBox( ghMPCompassBox ); + + // add strings for box + for(uiCounter=0; uiCounter < MAX_EDGES; uiCounter++) + { + + AddMonoString(&hStringHandle, gszMPEdgesText[uiCounter] ); + + // make sure it is unhighlighted + UnHighLightLine(hStringHandle); + } + + // set font type + SetBoxFont(ghMPCompassBox, MAP_SCREEN_FONT); + + // set highlight color + SetBoxHighLight(ghMPCompassBox, FONT_WHITE); + + // unhighlighted color + SetBoxForeground(ghMPCompassBox, FONT_LTGREEN); + + // background color + SetBoxBackground(ghMPCompassBox, FONT_BLACK); + + // shaded color..for darkened text + SetBoxShade( ghMPCompassBox, FONT_GRAY7 ); + SetBoxSecondaryShade( ghMPCompassBox, FONT_YELLOW ); + + // resize box to text + ResizeBoxToText( ghMPCompassBox ); + + //DetermineBoxPositions( ); +} +void CreateDestroyMouseRegionsForCompassBox() +{ + INT32 iFontHeight = 0; + INT32 iBoxXPosition = 0; + INT32 iBoxYPosition = 0; + INT32 iCounter = 0; + SGPPoint pPosition; + INT32 iBoxWidth = 0; + SGPRect pDimensions; + + if (!is_compass_box_open) + { + + // grab height of font + iFontHeight = GetLineSpace( ghMPCompassBox ) + GetFontHeight( GetBoxFont( ghMPCompassBox ) ); + + // get x.y position of box + GetBoxPosition( ghMPCompassBox, &pPosition); + + // grab box x and y position + iBoxXPosition = pPosition.iX; + iBoxYPosition = pPosition.iY; + + // get dimensions..mostly for width + GetBoxSize( ghMPCompassBox, &pDimensions ); + + // get width + iBoxWidth = pDimensions.iRight; + + SetCurrentBox( ghMPCompassBox ); + + // define regions + for( iCounter = 0; iCounter < MAX_EDGES; iCounter++ ) + { + // add mouse region for each line of text..and set user data + MSYS_DefineRegion( &gCompassMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghMPCompassBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghMPCompassBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , + MSYS_NO_CURSOR, CompassBoxMvtCallback, CompassBoxBtnCallback ); + + MSYS_SetRegionUserData( &gCompassMenuRegion[ iCounter ], 0, iCounter ); + } + } + else + { + // destroy regions + for( iCounter = 0; iCounter < MAX_EDGES; iCounter++ ) + { + // add mouse region for each line of text..and set user data + MSYS_RemoveRegion( &gCompassMenuRegion[ iCounter ] ); + } + } +} +void CompassBoxMvtCallback ( MOUSE_REGION* pRegion, INT32 iReason) +{ + // mvt callback handler for assignment region + INT32 iValue = -1; + + iValue = MSYS_GetRegionUserData( pRegion, 0 ); + + if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) + { + if( GetBoxShadeFlag( ghMPCompassBox, iValue ) == FALSE ) + { + HighLightBoxLine( ghMPCompassBox, iValue ); + } + } + else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) + { + // unhighlight all strings in box + UnHighLightBox( ghMPCompassBox ); + } +} +void CompassBoxBtnCallback ( MOUSE_REGION* pRegion, INT32 iReason) +{ + INT32 iValue = -1; + + iValue = MSYS_GetRegionUserData( pRegion, 0 ); + + + if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + UnHighLightBox( ghMPCompassBox ); + + /*switch( iValue ) + { + case MP_EDGE_NORTH: + break; + case MP_EDGE_SOUTH: + break; + case MP_EDGE_EAST: + break; + case MP_EDGE_WEST: + break; + }*/ + + // send edge change to all clients, if allowed :) + send_edgechange(iValue); + + + // close the box + CreateDestroyMouseRegionsForCompassBox(); + HideBox(ghMPCompassBox); + is_compass_box_open = false; + fTeamPanelDirty = true; + } + else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + // close the box + CreateDestroyMouseRegionsForCompassBox(); + HideBox(ghMPCompassBox); + is_compass_box_open = false; + fTeamPanelDirty = true; + } +} +void DestroyMPTeamBox( void ) +{ + RemoveBox(ghMPTeamBox); + ghMPTeamBox = -1; +} +void CreateMPTeamBox( void ) +{ + UINT32 hStringHandle; + UINT32 uiCounter; + + + // will create attribute pop up menu for mapscreen assignments + SGPPoint TeamPosition = { MP_TEAM_X, MP_ROWSTART_Y }; + SGPRect TeamDimensions = { 0,0, 100, 95}; + + + // create basic box + CreatePopUpBox(&ghMPTeamBox, TeamDimensions, TeamPosition, (POPUP_BOX_FLAG_CLIP_TEXT|POPUP_BOX_FLAG_CENTER_TEXT|POPUP_BOX_FLAG_RESIZE )); + + // which buffer will box render to + SetBoxBuffer(ghMPTeamBox, FRAME_BUFFER); + + // border type? + SetBorderType(ghMPTeamBox,guiPOPUPBORDERS); + + // background texture + SetBackGroundSurface(ghMPTeamBox, guiPOPUPTEX); + + // margin sizes + SetMargins(ghMPTeamBox, 6, 6, 4, 4 ); + + // space between lines + SetLineSpace(ghMPTeamBox, 2); + + // set current box to this one + SetCurrentBox( ghMPTeamBox ); + + // add strings for box + for(uiCounter=0; uiCounter < MAX_MP_TEAMS; uiCounter++) + { + + AddMonoString(&hStringHandle, gszMPTeamNames[uiCounter] ); + + // make sure it is unhighlighted + UnHighLightLine(hStringHandle); + } + + // set font type + SetBoxFont(ghMPTeamBox, MAP_SCREEN_FONT); + + // set highlight color + SetBoxHighLight(ghMPTeamBox, FONT_WHITE); + + // unhighlighted color + SetBoxForeground(ghMPTeamBox, FONT_LTGREEN); + + // background color + SetBoxBackground(ghMPTeamBox, FONT_BLACK); + + // shaded color..for darkened text + SetBoxShade( ghMPTeamBox, FONT_GRAY7 ); + SetBoxSecondaryShade( ghMPTeamBox, FONT_YELLOW ); + + // resize box to text + ResizeBoxToText( ghMPTeamBox ); + + //DetermineBoxPositions( ); +} +void CreateDestroyMouseRegionsForTeamBox() +{ + INT32 iFontHeight = 0; + INT32 iBoxXPosition = 0; + INT32 iBoxYPosition = 0; + INT32 iCounter = 0; + SGPPoint pPosition; + INT32 iBoxWidth = 0; + SGPRect pDimensions; + + if (!is_team_box_open) + { + + // grab height of font + iFontHeight = GetLineSpace( ghMPTeamBox ) + GetFontHeight( GetBoxFont( ghMPTeamBox ) ); + + // get x.y position of box + GetBoxPosition( ghMPTeamBox, &pPosition); + + // grab box x and y position + iBoxXPosition = pPosition.iX; + iBoxYPosition = pPosition.iY; + + // get dimensions..mostly for width + GetBoxSize( ghMPTeamBox, &pDimensions ); + + // get width + iBoxWidth = pDimensions.iRight; + + SetCurrentBox( ghMPTeamBox ); + + // define regions + for( iCounter = 0; iCounter < MAX_EDGES; iCounter++ ) + { + // add mouse region for each line of text..and set user data + MSYS_DefineRegion( &gTeamMenuRegion[ iCounter ], ( INT16 )( iBoxXPosition ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghMPTeamBox ) + ( iFontHeight ) * iCounter ), ( INT16 )( iBoxXPosition + iBoxWidth ), ( INT16 )( iBoxYPosition + GetTopMarginSize( ghMPTeamBox ) + ( iFontHeight ) * ( iCounter + 1 ) ), MSYS_PRIORITY_HIGHEST - 4 , + MSYS_NO_CURSOR, TeamBoxMvtCallback, TeamBoxBtnCallback ); + + MSYS_SetRegionUserData( &gTeamMenuRegion[ iCounter ], 0, iCounter ); + } + } + else + { + // destroy regions + for( iCounter = 0; iCounter < MAX_MP_TEAMS; iCounter++ ) + { + // add mouse region for each line of text..and set user data + MSYS_RemoveRegion( &gTeamMenuRegion[ iCounter ] ); + } + } +} +void TeamBoxMvtCallback ( MOUSE_REGION* pRegion, INT32 iReason) +{ + // mvt callback handler for assignment region + INT32 iValue = -1; + + iValue = MSYS_GetRegionUserData( pRegion, 0 ); + + if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) + { + if( GetBoxShadeFlag( ghMPTeamBox, iValue ) == FALSE ) + { + HighLightBoxLine( ghMPTeamBox, iValue ); + } + } + else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) + { + // unhighlight all strings in box + UnHighLightBox( ghMPTeamBox ); + } +} +void TeamBoxBtnCallback ( MOUSE_REGION* pRegion, INT32 iReason) +{ + INT32 iValue = -1; + + iValue = MSYS_GetRegionUserData( pRegion, 0 ); + + + if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + UnHighLightBox( ghMPTeamBox ); + + /*switch( iValue ) + { + case MP_EDGE_NORTH: + break; + case MP_EDGE_SOUTH: + break; + case MP_EDGE_EAST: + break; + case MP_EDGE_WEST: + break; + }*/ + + // send team change to all clients, if allowed :) + send_teamchange(iValue); + + + // close the box + CreateDestroyMouseRegionsForTeamBox(); + HideBox(ghMPTeamBox); + is_team_box_open = false; + fTeamPanelDirty = true; + } + else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) + { + // close the box + CreateDestroyMouseRegionsForTeamBox(); + HideBox(ghMPTeamBox); + is_team_box_open = false; + fTeamPanelDirty = true; + } +} // THIS IS STUFF THAT RUNS *ONCE* DURING APPLICATION EXECUTION, AT INITIAL STARTUP @@ -3716,6 +4283,12 @@ UINT32 MapScreenShutdown(void) // destroy some popup boxes fShowAssignmentMenu = FALSE; CreateDestroyAssignmentPopUpBoxes( ); + // OJW + if (is_networked) + { + DestroyMPCompassBox(); + DestroyMPTeamBox(); + } fShowContractMenu = FALSE; DetermineIfContractMenuCanBeShown( ); @@ -3827,6 +4400,9 @@ UINT32 MapScreenHandle(void) // handle the sort buttons AddTeamPanelSortButtonsForMapScreen( ); + // OJW - 20081204 + if (is_networked) + AddMPButtonsForMapScreen(); // load bottom graphics LoadMapScreenInterfaceBottom( ); @@ -3917,17 +4493,36 @@ UINT32 MapScreenHandle(void) CHECKF(AddVideoSurface( &vs_desc, &guiCHARLIST ));*/ VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; - if (iResolution == 0) + if (!is_networked) { - FilenameForBPP("INTERFACE\\newgoldpiece3.sti", VObjectDesc.ImageFile ); + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\newgoldpiece3.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\newgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\newgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + } } - else if (iResolution == 1) + else { - FilenameForBPP("INTERFACE\\newgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); - } - else if (iResolution == 2) - { - FilenameForBPP("INTERFACE\\newgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + // OJW - 20081204 - change mapscreen interface for MP games + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + } } CHECKF(AddVideoObject(&VObjectDesc, &guiCHARLIST)); @@ -4200,6 +4795,12 @@ UINT32 MapScreenHandle(void) CreateDestroyAssignmentPopUpBoxes( ); fShowAssignmentMenu = FALSE; + // OJW + if (is_networked) + { + CreateMPCompassBox(); + CreateMPTeamBox(); + } // create merc remove box CreateMercRemoveAssignBox( ); @@ -4305,6 +4906,25 @@ UINT32 MapScreenHandle(void) // don't process any input until we've been through here once if( gfFirstMapscreenFrame == FALSE ) { + // OJW - 20081129 + // Auto-start server or join server + if (is_networked) + { + // this function does nothing if + // already connected + NetworkAutoStart(); + + // handle changing picture on start game button + // for server + if (is_server) + { + if (allowlaptop && ButtonList [ giMapMPButton[3] ]->ImageNum == giMapMPButtonImage [4]) + { + // Change server's button to "ready" + SpecifyButtonImage( giMapMPButton [3] , giMapMPButtonImage [3] ); + } + } + } // Handle Interface uiNewScreen = HandleMapUI( ); if ( uiNewScreen != MAP_SCREEN ) @@ -5499,7 +6119,13 @@ UINT32 HandleMapUI( ) // added by haydent if (is_networked) + { ChangeSelectedMapSector( sMapX, sMapY, ( INT8 )iCurrentMapSectorZ ); + // OJW - 20081201 - change this state away from allowlaptop to its own field + // or something more seperate from that concept + if (is_server && !allowlaptop) + send_mapchange(); + } } else { @@ -6097,53 +6723,55 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent ) // ROMAN:0 multiplayer: Maybe we need to change the keys, because they are bound to other functions + // OJW - 20090210 - disabling the old method's for starting server and disconnecting + // as they are no longer needed case '1': { - if (is_networked) + /*if (is_networked) { // haydent start_server(); break; - } + }*/ } case '2': { - if (is_networked) + /*if (is_networked) { // haydent connect_client(); break; - } + }*/ } case '3': { - if (is_networked) + /*if (is_networked) { // haydent start_battle(); break; - } + }*/ } case '4': { - if (is_networked) + /*if (is_networked) { // haydent server_disconnect(); client_disconnect(); break; - } + }*/ } case '5': case '6': case '7': // WANNE: Only show the override panel when '7' is pressed - if (is_networked && InputEvent.usParam == '7') + /*if (is_networked && InputEvent.usParam == '7') { // haydent manual_overide(); break; - } + }*/ case '8': case '9': // multi-selects all characters in that squad. SHIFT key and 1-0 for squads 11-20 @@ -6818,6 +7446,15 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent ) fSAMSitesDisabledFromAttackingPlayer = !fSAMSitesDisabledFromAttackingPlayer; #endif } + // OJW - 20090208 + // Adding ingame chat + else + { + if (is_networked && is_client) + { + OpenChatMsgBox(); + } + } break; case 'z': @@ -6931,6 +7568,9 @@ void EndMapScreen( BOOLEAN fDuringFade ) // remove team panel sort button RemoveTeamPanelSortButtonsForMapScreen( ); + // OJW - 20081204 + if (is_networked) + RemoveMPButtonsForMapScreen( ); // for th merc insurance help text CreateDestroyInsuranceMouseRegionForMercs( FALSE ); @@ -6963,6 +7603,12 @@ void EndMapScreen( BOOLEAN fDuringFade ) // remove contract pop up box (always created upon mapscreen entry) RemoveBox(ghContractBox); ghContractBox = -1; + // OJW + if (is_networked) + { + DestroyMPCompassBox(); + DestroyMPTeamBox(); + } CreateDestroyAssignmentPopUpBoxes( ); CreateDestroyMilitiaControlPopUpBoxes( ); //lal @@ -7949,6 +8595,14 @@ void MAPInvClickCallback( MOUSE_REGION *pRegion, INT32 iReason ) UINT16 usOldItemIndex, usNewItemIndex; static BOOLEAN fRightDown = FALSE; + // OJW - 20090319 - fix merging bug on mapscreen + // If you left click a merge item such as EBR stock, then left click it on the item to merge, such as M-14... + // instead of right clicking (which is the correct action to merge) + // it will open an ItemDescriptionPanel, but then after that call PlaceObject, which performs a swap + // and overwites the gpItemDescObject pointer. Hence when the ItemDescriptionPanel is deleted + // it no longer references the right object, and does not necessarily free up all resources + // creating an error the next time it is called + bool bJustOpenedItemDescPanel = false; if (iReason & MSYS_CALLBACK_REASON_INIT) { @@ -8076,6 +8730,7 @@ void MAPInvClickCallback( MOUSE_REGION *pRegion, INT32 iReason ) // TOO PAINFUL TO DO!! --CC if ( !InItemDescriptionBox( ) ) { + bJustOpenedItemDescPanel = true; // OJW - 20090319 - fix merging on mapscreen - see top of function MAPInternalInitItemDescriptionBox( &(pSoldier->inv[ uiHandPos ]), 0, pSoldier ); } @@ -8110,6 +8765,8 @@ void MAPInvClickCallback( MOUSE_REGION *pRegion, INT32 iReason ) } // Else, try to place here + if ( !bJustOpenedItemDescPanel ) // OJW - 20090319 - fix merging on mapscreen - see top of function + { if ( PlaceObject( pSoldier, (UINT8)uiHandPos, gpItemPointer ) ) { @@ -8153,6 +8810,7 @@ void MAPInvClickCallback( MOUSE_REGION *pRegion, INT32 iReason ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_ITEM_PASSED_TO_MERC ], ShortItemNames[ usNewItemIndex ], pSoldier->name ); } + } } } } @@ -8656,7 +9314,12 @@ void CreateMouseRegionsForTeamList( void ) // the info region...is the background for the list itself - for( sCounter = 0; sCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCounter++ ) + // OJW - MP + int max_rows = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; + if (is_networked) + max_rows = 7; // check this value is correct / unhardcode it + + for( sCounter = 0; sCounter < max_rows; sCounter++ ) { if( sCounter >= FIRST_VEHICLE ) { @@ -8758,6 +9421,157 @@ void DestroyMouseRegionsForTeamList( void ) MSYS_RemoveRegion( &gTeamListContractRegion[ sCounter ]); } } +void MPReadyButtonCallback( GUI_BUTTON *btn, INT32 reason ) +{ + int iIndex = MSYS_GetBtnUserData(btn,0); + if (iIndex >= 1 || iIndex <= 4) + { + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags|=(BUTTON_CLICKED_ON); + } + else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + btn->uiFlags &= ~(BUTTON_CLICKED_ON); + + if (iIndex == 3 || iIndex == 4) + { + // ready / start button was clicked + if (is_networked) + { + start_battle(); + } + } + else if (iIndex == 2) + { + // compass button clicked + if (!is_compass_box_open) + { + if (can_edgechange()) + { + // open the box + CreateDestroyMouseRegionsForCompassBox(); + ShowBox(ghMPCompassBox); + is_compass_box_open = true; + } + else if (allowlaptop) + { + // warn the user that cannot change once buying has commenced + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + } + } + else + { + // close the box + CreateDestroyMouseRegionsForCompassBox(); + HideBox(ghMPCompassBox); + is_compass_box_open = false; + fTeamPanelDirty = true; + } + + } + else if (iIndex == 1) + { + // team button clicked + if (!is_team_box_open) + { + if (can_teamchange()) + { + // open the box + CreateDestroyMouseRegionsForTeamBox(); + ShowBox(ghMPTeamBox); + is_team_box_open = true; + } + else if (allowlaptop) + { + // warn the user that cannot change once buying has commenced + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + } + else if (RANDOM_SPAWN) + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"Cannot change edge, the game is set to random spawn"); + } + } + else + { + // close the box + CreateDestroyMouseRegionsForTeamBox(); + HideBox(ghMPTeamBox); + is_team_box_open = false; + fTeamPanelDirty = true; + } + } + } + } + } + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); +} +void MPCompassChangeCallback( MOUSE_REGION * pReason, INT32 iReason) +{ + if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + if (!is_compass_box_open) + { + if (can_edgechange()) + { + // open the box + CreateDestroyMouseRegionsForCompassBox(); + ShowBox(ghMPCompassBox); + is_compass_box_open = true; + } + else if (allowlaptop) + { + // warn the user that cannot change once buying has commenced + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + } + } + else + { + // close the box + CreateDestroyMouseRegionsForCompassBox(); + HideBox(ghMPCompassBox); + is_compass_box_open = false; + fTeamPanelDirty = true; + } + } +} +void MPTeamChangeCallback( MOUSE_REGION * pReason, INT32 iReason) +{ + if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + if (!is_team_box_open) + { + if (can_teamchange()) + { + // open the box + CreateDestroyMouseRegionsForTeamBox(); + ShowBox(ghMPTeamBox); + is_team_box_open = true; + } + else if (allowlaptop) + { + // warn the user that cannot change once buying has commenced + ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + } + else if (RANDOM_SPAWN) + { + ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"Cannot change edge, the game is set to random spawn"); + } + } + else + { + // close the box + CreateDestroyMouseRegionsForTeamBox(); + HideBox(ghMPTeamBox); + is_team_box_open = false; + fTeamPanelDirty = true; + } + } +} + +// OJW - End MP Mouse Regions and Callbacks // mask for mapscreen region @@ -10992,6 +11806,14 @@ void UpdateStatusOfMapSortButtons( void ) { HideButton( giMapSortButton[ iCounter ] ); } + // OJW - 20090210 - hide MP buttons here + if (is_networked) + { + for( iCounter = 0; iCounter < MAX_MP_BUTTONS; iCounter++ ) + { + HideButton( giMapMPButton[ iCounter ] ); + } + } if ( gfPreBattleInterfaceActive ) { HideButton( giCharInfoButton[ 0 ] ); @@ -11010,6 +11832,14 @@ void UpdateStatusOfMapSortButtons( void ) ShowButton( giMapSortButton[ iCounter ] ); } + // OJW - 20090210 - show MP buttons here + if (is_networked) + { + for( iCounter = 0; iCounter < MAX_MP_BUTTONS; iCounter++ ) + { + ShowButton( giMapMPButton[ iCounter ] ); + } + } ShowButton( giCharInfoButton[ 0 ] ); ShowButton( giCharInfoButton[ 1 ] ); @@ -11456,17 +12286,36 @@ BOOLEAN HandlePreloadOfMapGraphics( void ) CHECKF(AddVideoSurface( &vs_desc, &guiCHARLIST ));*/ VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; - if (iResolution == 0) + if (!is_networked) { - FilenameForBPP("INTERFACE\\newgoldpiece3.sti", VObjectDesc.ImageFile ); + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\newgoldpiece3.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\newgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\newgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + } } - else if (iResolution == 1) + else { - FilenameForBPP("INTERFACE\\newgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); - } - else if (iResolution == 2) - { - FilenameForBPP("INTERFACE\\newgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + // OJW - 20081204 - change mapscreen interface for MP games + if (iResolution == 0) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 1) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3_800x600.sti", VObjectDesc.ImageFile ); + } + else if (iResolution == 2) + { + FilenameForBPP("INTERFACE\\mpgoldpiece3_1024x768.sti", VObjectDesc.ImageFile ); + } } CHECKF(AddVideoObject(&VObjectDesc, &guiCHARLIST)); @@ -12089,8 +12938,41 @@ void AddTeamPanelSortButtonsForMapScreen( void ) return; } +void AddMPButtonsForMapScreen( void ) +{ + INT32 iCounter = 0; + //SGPFILENAME filename; + INT32 iImageIndex[ MAX_MP_BUTTONS+1 ] = { 0, 1, 3, 5 , 7 }; // sleep image is out or order (last) + INT32 iPressedIndex[ MAX_MP_BUTTONS+1 ] = { 0 , 2 , 4 , 6 , 8 }; + // add start game button and hide it... + for( iCounter = 0; iCounter < MAX_MP_BUTTONS+1; iCounter++ ) + { + giMapMPButtonImage[ iCounter ] = LoadButtonImage( "INTERFACE\\MPGOLDPIECEBUTTONS.STI", -1, iImageIndex[ iCounter ] , -1, iPressedIndex[ iCounter ] , -1 ); + + if (iCounter < MAX_MP_BUTTONS) + { + // buttonmake + giMapMPButton[ iCounter ]= QuickCreateButton( giMapMPButtonImage[ iCounter ], ( INT16 )( gMapMPButtonsX[ iCounter ] ), ( INT16 )( MP_BTN_Y ), + BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 5, + (GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)MPReadyButtonCallback ); + + // so we can tell which button was clicked + MSYS_SetBtnUserData(giMapMPButton[ iCounter ], 0, iCounter ); + } + + //SetButtonFastHelpText( giMapSortButton[ iCounter ], wMapScreenSortButtonHelpText[ iCounter ] ); + } + + if ((is_server || is_host) && !allowlaptop) + { + // Server has 'Start game' button showing first if the game hasnt started + SpecifyButtonImage( giMapMPButton [3] , giMapMPButtonImage [4] ); + } + + return; +} void SortListOfMercsInTeamPanel( BOOLEAN fRetainSelectedMercs, BOOLEAN fReverse ) { INT32 iCounter = 0, iCounterA = 0; @@ -12428,6 +13310,22 @@ void RemoveTeamPanelSortButtonsForMapScreen( void ) return; } +void RemoveMPButtonsForMapScreen( void ) +{ + INT32 iCounter = 0; + + for( iCounter = 0; iCounter < MAX_MP_BUTTONS+1; iCounter++ ) + { + UnloadButtonImage( giMapMPButtonImage[ iCounter ] ); + + if(iCounter < MAX_MP_BUTTONS) + RemoveButton( giMapMPButton[ iCounter ] ); + + giMapMPButtonImage[ iCounter ] = -1; + giMapMPButton[ iCounter ] = -1; + } + return; +} void HandleCommonGlowTimer( ) { @@ -13661,7 +14559,8 @@ BOOLEAN CanMoveBullseyeAndClickedOnIt( INT16 sMapX, INT16 sMapY ) if (is_networked) { // haydent - if (!is_server && !is_client) + // OJW 080101 - allow map change before laptop unlock + if ((!is_server && !is_client) || (is_server && !allowlaptop)) { // if he clicked on the bullseye, and we're on the surface level if ( ( sMapX == gsMercArriveSectorX ) && ( sMapY == gsMercArriveSectorY ) && ( iCurrentMapSectorZ == 0 ) ) diff --git a/Strategic/mapscreen.h b/Strategic/mapscreen.h index de248330..e10a43d3 100644 --- a/Strategic/mapscreen.h +++ b/Strategic/mapscreen.h @@ -42,6 +42,7 @@ extern INT8 NUM_TOWNS; extern BOOLEAN fCharacterInfoPanelDirty; extern BOOLEAN fTeamPanelDirty; extern BOOLEAN fMapPanelDirty; +extern BOOLEAN fDrawCharacterList; // OJW - 20081204 extern BOOLEAN fMapInventoryItem; extern BOOLEAN gfInConfirmMapMoveMode; diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index e25a7ae0..34c4c7f0 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -6218,21 +6218,29 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) return( FALSE ); } - // We can only check for end of battle if in combat mode or there are enemies - // present (they might bleed to death or run off the map!) - if ( ! ( gTacticalStatus.uiFlags & INCOMBAT ) ) + // OJW - 090212 - Fix end conditions for multiplayer - TeamDM + if(is_server) { - if ( ! (gTacticalStatus.fEnemyInSector) ) + // check the server's conditions for continueing the game, if the server wants to continue the game it returns true + // hence we return false that the battle has ended. If not, when this function returns below we will force the game to end. + if ( check_status() ) + return(FALSE); + // the block of code below would always cause this function to exit before checking the servers desires + // in cases of team deathmatch where there were no more enemies + } + else + { + // We can only check for end of battle if in combat mode or there are enemies + // present (they might bleed to death or run off the map!) + if ( ! ( gTacticalStatus.uiFlags & INCOMBAT ) ) { - return( FALSE ); + if ( ! (gTacticalStatus.fEnemyInSector) ) + { + return( FALSE ); + } } } - if(is_server && check_status()) - { - return(FALSE); - } - // ATE: If attack busy count.. get out... if ( (gTacticalStatus.ubAttackBusyCount > 0 ) ) { @@ -6329,6 +6337,10 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) //Whenever returning TRUE, make sure you clear gfBlitBattleSectorLocator; LogBattleResults( LOG_DEFEAT ); gfBlitBattleSectorLocator = FALSE; + // If we are the server, we escape this function at the top if we think the game should still be running + // hence if we get here the game is over for all clients and we should report it + if (is_networked && is_server) + game_over(); return( TRUE ); } @@ -6438,11 +6450,15 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) { SetMusicMode( MUSIC_TACTICAL_VICTORY ); - ShouldBeginAutoBandage( ); + // OJW - 20081222 - dont auto-bandage if networked + if (!is_networked) + ShouldBeginAutoBandage( ); } else if ( gfLastMercTalkedAboutKillingID != NOBODY && ( MercPtrs[ gfLastMercTalkedAboutKillingID ]->flags.uiStatusFlags & SOLDIER_MONSTER ) ) { - ShouldBeginAutoBandage( ); + // OJW - 20081222 - dont auto-bandage if networked + if (!is_networked) + ShouldBeginAutoBandage( ); } // Say battle end quote.... @@ -6474,7 +6490,9 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) { // Change to nothing music... SetMusicMode( MUSIC_TACTICAL_NOTHING ); - ShouldBeginAutoBandage(); + // OJW - 20081222 - dont auto bandage if networked + if (!is_networked) + ShouldBeginAutoBandage(); } HandleMilitiaStatusInCurrentMapBeforeLoadingNewMap(); @@ -6550,8 +6568,16 @@ BOOLEAN CheckForEndOfBattle( BOOLEAN fAnEnemyRetreated ) if(gGameExternalOptions.gfRevealItems) RevealAllDroppedEnemyItems(); + // If we are the server, we escape this function at the top if we think the game should still be running + // hence if we get here the game is over for all clients and we should report it + if (is_networked && is_server) + game_over(); return( TRUE ); } + // If we are the server, we escape this function at the top if we think the game should still be running + // hence if we get here the game is over for all clients and we should report it + if (is_networked && is_server) + game_over(); return( FALSE ); } diff --git a/Tactical/Soldier Ani.cpp b/Tactical/Soldier Ani.cpp index 965013e8..cbe7272d 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -3692,6 +3692,13 @@ BOOLEAN CheckForAndHandleSoldierDyingNotFromHit( SOLDIERTYPE *pSoldier ) // Increment being attacked count // pSoldier->bBeingAttackedCount++; + // OJW - Send bleeding death + if (is_networked) + { + if(pSoldier->bTeam==0) send_death(pSoldier); + else if(pSoldier->bTeam <6 && ((gTacticalStatus.ubTopMessageType == PLAYER_TURN_MESSAGE) || (gTacticalStatus.ubTopMessageType == PLAYER_INTERRUPT_MESSAGE)))send_death(pSoldier); + else if (pSoldier->bTeam < 6 && (is_server)) send_death(pSoldier); + } if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] ) { switch( pSoldier->usAnimState ) diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index ae087a0c..d210c80d 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -530,8 +530,7 @@ void BeginTeamTurn( UINT8 ubTeam ) } break; } - else - if (ubTeam > 4 || (is_client && !is_server )) //hayden + else if (ubTeam > 4 || (is_client && !is_server )) //hayden { InitEnemyUIBar( 0, 0 ); diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index b7fe2b7f..4fbc2cbc 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -1643,7 +1643,14 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } } } - } + + // OJW - 090209 - ingame chat + if (InputEvent.usEvent == KEY_UP && InputEvent.usParam == 'y') + { + OpenChatMsgBox(); + continue; + } + } // end is_networked // Break of out IN CONV... diff --git a/Utils/Debug Control.cpp b/Utils/Debug Control.cpp index 21a5eac1..2a36a417 100644 --- a/Utils/Debug Control.cpp +++ b/Utils/Debug Control.cpp @@ -67,3 +67,13 @@ void LiveMessage( CHAR8 *strMessage) fclose(OutFile); } } +void MPDebugMsg( CHAR8 *strMessage) +{ + FILE *OutFile; + + if ((OutFile = fopen("MPDebug.txt", "a+t")) != NULL) + { + fprintf(OutFile, "%s\n", strMessage); + fclose(OutFile); + } +} diff --git a/Utils/Debug Control.h b/Utils/Debug Control.h index 257caaa7..8d562fa5 100644 --- a/Utils/Debug Control.h +++ b/Utils/Debug Control.h @@ -14,6 +14,7 @@ void LiveMessage( CHAR8 *strMessage); +void MPDebugMsg( CHAR8 *strMessage); #ifdef _ANIMSUBSYSTEM_DEBUG diff --git a/Utils/Text.h b/Utils/Text.h index 281cec0d..7d78b76b 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -1436,7 +1436,104 @@ enum }; extern STR16 gzGIOScreenText[]; +// OJW - 20081129 +// Multiplayer Join Screen +enum +{ + MPJ_TITLE_TEXT, + MPJ_JOIN_TEXT, + MPJ_HOST_TEXT, + MPJ_CANCEL_TEXT, + MPJ_REFRESH_TEXT, + MPJ_HANDLE_TEXT, + MPJ_SERVERIP_TEXT, + MPJ_SERVERPORT_TEXT, + MPJ_SERVERNAME_TEXT, + MPJ_NUMPLAYERS_TEXT, + MPJ_SERVERVER_TEXT, + MPJ_GAMETYPE_TEXT, + MPJ_PING_TEXT, + MPJ_HANDLE_INVALID, + MPJ_SERVERIP_INVALID, + MPJ_SERVERPORT_INVALID +}; +extern STR16 gzMPJScreenText[]; +//Multiplayer Host Screen +enum +{ + MPH_TITLE_TEXT, + MPH_START_TEXT, + MPH_CANCEL_TEXT, + MPH_SERVERNAME_TEXT, + MPH_GAMETYPE_TEXT, + MPH_DEATHMATCH_TEXT, + MPH_TEAMDM_TEXT, + MPH_COOP_TEXT, + MPH_NUMPLAYERS_TEXT, + MPH_SQUADSIZE_TEXT, + MPH_MERCSELECT_TEXT, + MPH_RANDOMMERCS_TEXT, + MPH_PLAYERMERCS_TEXT, + MPH_BALANCE_TEXT, + MPH_SAMEMERC_TEXT, + MPH_RPTMERC_TEXT, + MPH_BOBBYRAY_TEXT, + MPH_RNDMSTART_TEXT, + MPH_SERVERNAME_INVALID, + MPH_MAXPLAYERS_INVALID, + MPH_SQUADSIZE_INVALID, + MPH_TIME_TEXT, + MPH_TIME_INVALID, + MPH_CASH_INVALID, + MPH_DMG_TEXT, + MPH_DMG_INVALID, + MPH_TIMER_TEXT, + MPH_TIMER_INVALID, + MPH_ENABLECIV_TEXT, + MPH_USENIV_TEXT, +}; +extern STR16 gzMPHScreenText[]; +enum +{ + MPS_TITLE_TEXT, + MPS_CONTINUE_TEXT, + MPS_CANCEL_TEXT, + MPS_PLAYER_TEXT, + MPS_KILLS_TEXT, + MPS_DEATHS_TEXT, + MPS_AITEAM_TEXT, + MPS_HITS_TEXT, + MPS_MISSES_TEXT, + MPS_ACCURACY_TEXT, + MPS_DMGDONE_TEXT, + MPS_DMGTAKEN_TEXT, +}; +extern STR16 gzMPSScreenText[]; +// Multiplayer Starting Edges +enum +{ + MP_EDGE_NORTH, + MP_EDGE_SOUTH, + MP_EDGE_EAST, + MP_EDGE_WEST, + MAX_EDGES, +}; +extern STR16 gszMPEdgesText[]; +// MP TEAM NAMES +enum +{ + MP_TEAM_1, + MP_TEAM_2, + MP_TEAM_3, + MP_TEAM_4, + MAX_MP_TEAMS, +}; +extern STR16 gszMPTeamNames[]; + +extern STR16 gzMPChatToggleText[]; + +extern STR16 gzMPChatboxText[]; enum { @@ -1608,4 +1705,6 @@ enum NIV_NO_CLIMB, }; +// OJW - MP +extern STR16 gszMPMapscreenText[]; #endif diff --git a/Utils/Timer Control.h b/Utils/Timer Control.h index 2e1b29f2..777ed2d0 100644 --- a/Utils/Timer Control.h +++ b/Utils/Timer Control.h @@ -82,6 +82,7 @@ void CheckCustomizableTimer( void ); //Don't modify this value extern UINT32 guiBaseJA2Clock; +extern UINT32 guiBaseJA2NoPauseClock; extern CUSTOMIZABLE_TIMER_CALLBACK gpCustomizableTimerCallback; // MACROS diff --git a/Utils/Utilities.cpp b/Utils/Utilities.cpp index acb3709f..818304aa 100644 --- a/Utils/Utilities.cpp +++ b/Utils/Utilities.cpp @@ -371,6 +371,7 @@ UINT32 gCheckFileMinSizes[] = 187000000, 236000000 }; +#define NOCDCHECK #if defined( JA2TESTVERSION ) || defined( _DEBUG ) #define NOCDCHECK diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 7e596738..1ff659cf 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -4572,6 +4572,60 @@ STR16 gzGIOScreenText[] = L"游戏初始设置(仅在服务器设置时有效)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"奥斯汀", //"Austin", //Austin, Texas, USA @@ -5477,6 +5531,22 @@ STR16 MPClientMessage[] = L"你已被击败!", L"对不起, 在多人游戏中无法攀登。", L"你雇佣了 '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -5504,6 +5574,62 @@ STR16 MPHelp[] = L"'F1' - 显示基本帮助", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + // WANNE: Some Chinese specific strings that needs to be in unicode! STR16 ChineseSpecString1 = L"%%"; //defined in _ChineseText.cpp as this file is already unicode STR16 ChineseSpecString2 = L"*%3d%%%%"; //defined in _ChineseText.cpp as this file is already unicode diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 2b4ad606..b292a98f 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -3797,6 +3797,60 @@ STR16 gzGIOScreenText[] = L"INITIAL GAME SETTINGS (Only the server settings take effect)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4530,6 +4584,22 @@ STR16 MPClientMessage[] = L"You have been defeated!", L"Sorry, climbing is disable in MP", L"You Hired '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4557,4 +4627,60 @@ STR16 MPHelp[] = L"'F1' - Display primary help", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 4d7892fc..181d0f22 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -3810,6 +3810,60 @@ STR16 gzGIOScreenText[] = L"INITIAL GAME SETTINGS (Only the server settings take effect)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4543,6 +4597,22 @@ STR16 MPClientMessage[] = L"You have been defeated!", L"Sorry, climbing is disable in MP", L"You Hired '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4570,4 +4640,59 @@ STR16 MPHelp[] = L"'F1' - Display primary help", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 78388be1..9f33b34a 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -3811,6 +3811,60 @@ STR16 gzGIOScreenText[] = L"CONFIGURATION DU JEU (Les paramtres serveur seulement prennent effet)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4544,6 +4598,22 @@ STR16 MPClientMessage[] = L"You have been defeated!", L"Sorry, climbing is disable in MP", L"You Hired '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4571,4 +4641,60 @@ STR16 MPHelp[] = L"'F1' - Display primary help", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index cd22b377..abf6291a 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -3615,6 +3615,60 @@ STR16 gzGIOScreenText[] = L"GRUNDEINSTELLUNGEN (Nur Servereinstellungen werden verwendet)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4343,6 +4397,22 @@ STR16 MPClientMessage[] = L"Sie wurden besiegt!", L"Auf Dcher klettern ist nicht erlaubt in einem Mehrspieler Spiel", L"Sie haben '%s' angeheuert.", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4370,4 +4440,60 @@ STR16 MPHelp[] = L"'F1' - Anzeige der primren Hilfe", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 9a211254..333818b9 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -3790,6 +3790,60 @@ STR16 gzGIOScreenText[] = L"INITIAL GAME SETTINGS (Only the server settings take effect)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4528,6 +4582,22 @@ STR16 MPClientMessage[] = L"You have been defeated!", L"Sorry, climbing is disable in MP", L"You Hired '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4555,4 +4625,60 @@ STR16 MPHelp[] = L"'F1' - Display primary help", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index b83fc856..2e348a50 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -3803,6 +3803,60 @@ STR16 gzGIOScreenText[] = L"POCZTKOWE USTAWIENIA GRY (Only the server settings take effect)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4535,6 +4589,22 @@ STR16 MPClientMessage[] = L"Zostae pokonany!", L"Wspinanie wyczone w MP", L"Wynajto '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4562,4 +4632,60 @@ STR16 MPHelp[] = L"'F1' - Wywietl 1. pomoc", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 4b167316..3b13e8c4 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -3805,6 +3805,60 @@ STR16 gzGIOScreenText[] = L" ( )", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"", //Austin, Texas, USA @@ -4539,6 +4593,22 @@ STR16 MPClientMessage[] = L" !", L", .", L" %s.", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4566,4 +4636,60 @@ STR16 MPHelp[] = L"'F1' - .", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //RUSSIAN diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index 739f3bdd..d2b3221d 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -3810,6 +3810,60 @@ STR16 gzGIOScreenText[] = L"INITIAL GAME SETTINGS (Only the server settings take effect)", }; +STR16 gzMPJScreenText[] = +{ + L"MULTIPLAYER", + L"Join", + L"Host", + L"Cancel", + L"Refresh", + L"Player Name", + L"Server IP", + L"Port", + L"Server Name", + L"# Plrs", + L"Version", + L"Game Type", + L"Ping", + L"You must enter a player name", + L"You must enter a valid server IP address.\n (eg 192.168.0.1)", + L"You must enter a valid Server Port between 1 and 65535" +}; + +STR16 gzMPHScreenText[] = +{ + L"HOST GAME", + L"Start", + L"Cancel", + L"Server Name", + L"Game Type", + L"Deathmatch", + L"Team Deathmatch", + L"Co-operative", + L"Max Players", + L"Squad Size", + L"Merc Selection", + L"Random Mercs", + L"Hired by Player", + L"Starting Balance", + L"Can Hire Same Merc", + L"Report Hired Mercs", + L"Allow Bobby Rays", + L"Randomise Starting Edge", + L"You must enter a server name", + L"Max Players must be between 2 and 4", + L"Squad size must be between 1 and 6", + L"Time of Day", + L"Time of Day must be a 24 hr time (HH:MM)\n\n eg. 13:30 = 1.30pm", + L"Starting Cash must be a valid dollar amount ( no cents )\n\n eg. 150000" , + L"Damage Multiplier", + L"Damage Multiplier must be a number between 0 and 5", + L"Turn Timer Multiplier", + L"Turn Timer multiplier must be a number between 1 and 200", + L"Enable Civilians in CO-OP", + L"Use New Inventory (NIV)", +}; + STR16 pDeliveryLocationStrings[] = { L"Austin", //Austin, Texas, USA @@ -4542,6 +4596,22 @@ STR16 MPClientMessage[] = L"You have been defeated!", L"Sorry, climbing is disable in MP", L"You Hired '%s'", + // 45 + L"You cant change the map once purchasing has commenced", + L"Map changed to '%s'", + L"Client '%s' disconnected, removing from game", + L"You were disconnected from the game, returning to the Main Menu", + L"Connection failed, Retrying in 5 seconds, %i retries left...", + //50 + L"Connection failed, giving up...", + L"You cannot start the game until another player has connected", + L"%s : %s", + L"Send to All", + L"Allies only", + // 55 + L"Cannot join game. This game has already started.", + L"%s (team): %s", + L"Client #%i - '%s'", }; STR16 MPHelp[] = @@ -4569,4 +4639,60 @@ STR16 MPHelp[] = L"'F1' - Display primary help", }; +STR16 gszMPEdgesText[] = +{ + L"N", + L"S", + L"E", + L"W" +}; + +STR16 gszMPTeamNames[] = +{ + L"Foxtrot", + L"Bravo", + L"Delta", + L"Charlie" +}; + +STR16 gszMPMapscreenText[] = +{ + L"Game Type: ", + L"Players: ", + L"Mercs each: ", + L"You cannot change starting edge once Laptop is unlocked", + L"You cannot change teams once the Laptop is unlocked", + L"Random Mercs: ", + L"Y", + L"Difficulty:" +}; + +STR16 gzMPSScreenText[] = +{ + L"Scoreboard", + L"Continue", + L"Cancel", + L"Player", + L"Kills", + L"Deaths", + L"Queen's Army", + L"Hits", + L"Misses", + L"Accuracy", + L"Damage Dealt", + L"Damage Taken" +}; + +STR16 gzMPChatToggleText[] = +{ + L"Send to All", + L"Send to Allies only", +}; + +STR16 gzMPChatboxText[] = +{ + L"Multiplayer Chat", + L"Chat: press ENTER to send of ESC to cancel", +}; + #endif //TAIWANESE diff --git a/Utils/message.h b/Utils/message.h index 85b84594..d8aeedca 100644 --- a/Utils/message.h +++ b/Utils/message.h @@ -69,6 +69,15 @@ UINT8 GetRangeOfMapScreenMessages( void ); void EnableDisableScrollStringVideoOverlay( BOOLEAN fEnable ); +// OJW - 20090315 - Allow access to these functions outside of message +// adding chat log +ScrollStringStPtr GetNextString(ScrollStringStPtr pStringSt); +ScrollStringStPtr GetPrevString(ScrollStringStPtr pStringSt); +void SetStringPosition(ScrollStringStPtr pStringSt, UINT16 x, UINT16 y); +void SetStringColor(ScrollStringStPtr pStringSt, UINT16 color); +ScrollStringStPtr SetStringNext(ScrollStringStPtr pStringSt, ScrollStringStPtr pNext); +ScrollStringStPtr SetStringPrev(ScrollStringStPtr pStringSt, ScrollStringStPtr pPrev); +void SetString(ScrollStringStPtr pStringSt, STR16 String); // will go and clear all displayed strings off the screen void ClearDisplayedListOfTacticalStrings( void ); diff --git a/gameloop.cpp b/gameloop.cpp index cecb9fe1..21599f48 100644 --- a/gameloop.cpp +++ b/gameloop.cpp @@ -56,6 +56,7 @@ extern BOOLEAN gfTacticalPlacementGUIActive; extern BOOLEAN gfTacticalPlacementGUIDirty; extern BOOLEAN gfValidLocationsChanged; extern BOOLEAN gfInMsgBox; +extern BOOLEAN gfInChatBox; // OJW - 20090314 - new chatbox extern void InitSightRange(); //lal @@ -152,7 +153,10 @@ BOOLEAN InitializeGame(void) //InitGameOptions(); // preload mapscreen graphics - HandlePreloadOfMapGraphics( ); + // OJW - Temporarily disabliing this as is_networked is not set up yet + // see if there is a better place we can pre-load the gfx if nessesary + // or perhaps just reload the bits that will change + //HandlePreloadOfMapGraphics( ); guiCurrentScreen = INIT_SCREEN; @@ -300,6 +304,11 @@ void GameLoop(void) guiPendingScreen = MSG_BOX_SCREEN; } + // OJW - 20090314 - new chatbox + if (gfInChatBox) + { + guiPendingScreen = MP_CHAT_SCREEN; + } if ( guiPendingScreen != NO_PENDING_SCREEN ) { // Based on active screen, deinit! @@ -308,7 +317,7 @@ void GameLoop(void) switch( guiCurrentScreen ) { case MAP_SCREEN: - if( guiPendingScreen != MSG_BOX_SCREEN ) + if( guiPendingScreen != MSG_BOX_SCREEN && guiPendingScreen != MP_CHAT_SCREEN ) { EndMapScreen( FALSE ); } @@ -411,7 +420,7 @@ extern UINT32 guiRainLoop; void HandleNewScreenChange( UINT32 uiNewScreen, UINT32 uiOldScreen ) { //if we are not going into the message box screen, and we didnt just come from it - if( ( uiNewScreen != MSG_BOX_SCREEN && uiOldScreen != MSG_BOX_SCREEN ) ) + if( ( uiNewScreen != MSG_BOX_SCREEN && uiOldScreen != MSG_BOX_SCREEN && uiNewScreen != MP_CHAT_SCREEN && uiOldScreen != MP_CHAT_SCREEN ) ) { //reset the help screen NewScreenSoResetHelpScreen( ); diff --git a/gamescreen.cpp b/gamescreen.cpp index 0b329622..e24aba5d 100644 --- a/gamescreen.cpp +++ b/gamescreen.cpp @@ -654,7 +654,7 @@ UINT32 MainGameScreenHandle(void) return( GAME_SCREEN ); } - if ( guiCurrentScreen != MSG_BOX_SCREEN ) + if ( guiCurrentScreen != MSG_BOX_SCREEN && guiCurrentScreen != MP_CHAT_SCREEN ) { if ( HandleBeginFadeOut( GAME_SCREEN ) ) { diff --git a/ja2_2005Express.vcproj b/ja2_2005Express.vcproj index a163fc06..987fbb9c 100644 --- a/ja2_2005Express.vcproj +++ b/ja2_2005Express.vcproj @@ -66,7 +66,7 @@ + + + + + + + + @@ -550,6 +566,22 @@ RelativePath=".\MessageBoxScreen.h" > + + + + + + + + diff --git a/ja2_VS2008.vcproj b/ja2_VS2008.vcproj index ecfea221..0420a83f 100644 --- a/ja2_VS2008.vcproj +++ b/ja2_VS2008.vcproj @@ -435,6 +435,22 @@ RelativePath="MessageBoxScreen.h" > + + + + + + + + @@ -597,6 +613,22 @@ RelativePath="MessageBoxScreen.cpp" > + + + + + + + + diff --git a/jascreens.h b/jascreens.h index 265ed9f7..33ccbab3 100644 --- a/jascreens.h +++ b/jascreens.h @@ -118,6 +118,24 @@ extern UINT32 CreditScreenInit( void ); extern UINT32 CreditScreenHandle( void ); extern UINT32 CreditScreenShutdown( void ); +// OJW - 20081129 +extern UINT32 MPJoinScreenInit( void ); +extern UINT32 MPJoinScreenHandle( void ); +extern UINT32 MPJoinScreenShutdown( void ); + +extern UINT32 MPHostScreenInit( void ); +extern UINT32 MPHostScreenHandle( void ); +extern UINT32 MPHostScreenShutdown( void ); + +// OJW - 20081222 +extern UINT32 MPScoreScreenInit( void ); +extern UINT32 MPScoreScreenHandle( void ); +extern UINT32 MPScoreScreenShutdown( void ); + +// OJW - 20090314 +extern UINT32 MPChatScreenInit( void ); +extern UINT32 MPChatScreenHandle( void ); +extern UINT32 MPChatScreenShutdown( void ); // External functions void DisplayFrameRate( ); diff --git a/screenids.h b/screenids.h index 67168194..7e7f86a1 100644 --- a/screenids.h +++ b/screenids.h @@ -28,6 +28,10 @@ typedef enum ScreenTypes DEMO_EXIT_SCREEN, INTRO_SCREEN, CREDIT_SCREEN, + MP_JOIN_SCREEN, // OJW - 20081129 + MP_HOST_SCREEN, + MP_SCORE_SCREEN, // OJW - 20081222 + MP_CHAT_SCREEN, // OJW - 20090315 #ifdef JA2BETAVERSION AIVIEWER_SCREEN,