warzone2100/src/multiint.cpp

4514 lines
130 KiB
C++
Raw Normal View History

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* MultiInt.c
*
* Alex Lee, 98. Pumpkin Studios, Bath.
* Functions to display and handle the multiplayer interface screens,
* along with connection and game options.
*/
#include "lib/framework/wzapp.h"
#include "lib/framework/wzconfig.h"
#include <time.h>
#include "lib/framework/frameresource.h"
#include "lib/framework/file.h"
#include "lib/framework/stdio_ext.h"
/* Includes direct access to render library */
#include "lib/ivis_opengl/bitimage.h"
#include "lib/ivis_opengl/pieblitfunc.h"
#include "lib/ivis_opengl/piedef.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/pieclip.h"
#include "lib/ivis_opengl/piemode.h"
#include "lib/ivis_opengl/piepalette.h"
#include "lib/ivis_opengl/screen.h"
#include "lib/gamelib/gtime.h"
#include "lib/netplay/netplay.h"
#include "lib/script/script.h"
#include "lib/widget/editbox.h"
#include "lib/widget/button.h"
#include "lib/widget/widget.h"
#include "lib/widget/widgint.h"
#include "lib/widget/label.h"
#include "challenge.h"
#include "main.h"
#include "objects.h"
#include "display.h"// pal stuff
#include "display3d.h"
#include "objmem.h"
#include "gateway.h"
#include "configuration.h"
#include "intdisplay.h"
#include "design.h"
#include "hci.h"
#include "power.h"
#include "loadsave.h" // for blueboxes.
#include "component.h"
#include "map.h"
#include "console.h" // chat box stuff
#include "frend.h"
#include "advvis.h"
#include "frontend.h"
#include "data.h"
#include "keymap.h"
#include "game.h"
#include "warzoneconfig.h"
#include "modding.h"
2011-01-23 13:30:57 -08:00
#include "qtscript.h"
#include "random.h"
#include "multiplay.h"
#include "multiint.h"
#if defined(WZ_CC_MSVC)
#include "multiint_moc.h" // this is generated on the pre-build event.
#endif
#include "multijoin.h"
#include "multistat.h"
#include "multirecv.h"
#include "multimenu.h"
#include "multilimit.h"
#include "multigifts.h"
#include "warzoneconfig.h"
#include "init.h"
#include "levels.h"
#include "wrappers.h"
#define MAP_PREVIEW_DISPLAY_TIME 2500 // number of milliseconds to show map in preview
// ////////////////////////////////////////////////////////////////////////////
// tertile dependant colors for map preview
// C1 - Arizona type
#define WZCOL_TERC1_CLIFF_LOW pal_Colour(0x68, 0x3C, 0x24)
#define WZCOL_TERC1_CLIFF_HIGH pal_Colour(0xE8, 0x84, 0x5C)
#define WZCOL_TERC1_WATER pal_Colour(0x3F, 0x68, 0x9A)
#define WZCOL_TERC1_ROAD_LOW pal_Colour(0x24, 0x1F, 0x16)
#define WZCOL_TERC1_ROAD_HIGH pal_Colour(0xB2, 0x9A, 0x66)
#define WZCOL_TERC1_GROUND_LOW pal_Colour(0x24, 0x1F, 0x16)
#define WZCOL_TERC1_GROUND_HIGH pal_Colour(0xCC, 0xB2, 0x80)
// C2 - Urban type
#define WZCOL_TERC2_CLIFF_LOW pal_Colour(0x3C, 0x3C, 0x3C)
#define WZCOL_TERC2_CLIFF_HIGH pal_Colour(0x84, 0x84, 0x84)
#define WZCOL_TERC2_WATER WZCOL_TERC1_WATER
#define WZCOL_TERC2_ROAD_LOW pal_Colour(0x00, 0x00, 0x00)
#define WZCOL_TERC2_ROAD_HIGH pal_Colour(0x24, 0x1F, 0x16)
#define WZCOL_TERC2_GROUND_LOW pal_Colour(0x1F, 0x1F, 0x1F)
#define WZCOL_TERC2_GROUND_HIGH pal_Colour(0xB2, 0xB2, 0xB2)
// C3 - Rockies type
#define WZCOL_TERC3_CLIFF_LOW pal_Colour(0x3C, 0x3C, 0x3C)
#define WZCOL_TERC3_CLIFF_HIGH pal_Colour(0xFF, 0xFF, 0xFF)
#define WZCOL_TERC3_WATER WZCOL_TERC1_WATER
#define WZCOL_TERC3_ROAD_LOW pal_Colour(0x24, 0x1F, 0x16)
#define WZCOL_TERC3_ROAD_HIGH pal_Colour(0x3D, 0x21, 0x0A)
#define WZCOL_TERC3_GROUND_LOW pal_Colour(0x00, 0x1C, 0x0E)
#define WZCOL_TERC3_GROUND_HIGH WZCOL_TERC3_CLIFF_HIGH
2011-01-03 06:27:06 -08:00
static const unsigned gnImage[] = {IMAGE_GN_0, IMAGE_GN_1, IMAGE_GN_2, IMAGE_GN_3, IMAGE_GN_4, IMAGE_GN_5, IMAGE_GN_6, IMAGE_GN_7, IMAGE_GN_8, IMAGE_GN_9, IMAGE_GN_10, IMAGE_GN_11, IMAGE_GN_12, IMAGE_GN_13, IMAGE_GN_14, IMAGE_GN_15};
// ////////////////////////////////////////////////////////////////////////////
// vars
extern char MultiCustomMapsPath[PATH_MAX];
extern char MultiPlayersPath[PATH_MAX];
extern bool bSendingMap; // used to indicate we are sending a map
bool bHosted = false; //we have set up a game
char sPlayer[128]; // player name (to be used)
static int colourChooserUp = -1;
static int teamChooserUp = -1;
static int aiChooserUp = -1;
static int difficultyChooserUp = -1;
static int positionChooserUp = -1;
static bool SettingsUp = false;
static UBYTE InitialProto = 0;
static W_SCREEN *psConScreen;
static SDWORD dwSelectedGame =0; //player[] and games[] indexes
static UDWORD gameNumber; // index to games icons
static bool safeSearch = false; // allow auto game finding.
static bool disableLobbyRefresh = false; // if we allow lobby to be refreshed or not.
static UDWORD hideTime=0;
static bool EnablePasswordPrompt = false; // if we need the password prompt
LOBBY_ERROR_TYPES LobbyError = ERROR_NOERROR;
static char tooltipbuffer[MaxGames][256] = {{'\0'}};
static bool toggleFilter = true; // Used to show all games or only games that are of the same version
/// end of globals.
// ////////////////////////////////////////////////////////////////////////////
// Function protos
// widget functions
static bool addMultiEditBox(UDWORD formid, UDWORD id, UDWORD x, UDWORD y, char const *tip, char const *tipres, UDWORD icon, UDWORD iconhi, UDWORD iconid);
static void addBlueForm (UDWORD parent,UDWORD id, const char *txt,UDWORD x,UDWORD y,UDWORD w,UDWORD h);
static void drawReadyButton(UDWORD player);
// Drawing Functions
static void displayChatEdit (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void intDisplayFeBox (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayRemoteGame (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayPlayer (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayPosition (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayColour (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayTeamChooser (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayAi (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayDifficulty (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static void displayMultiEditBox (WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset);
static Image getFrontHighlightImage(Image image);
// find games
static void addGames (void);
static void removeGames (void);
// password form functions
static void hidePasswordForm(void);
static void showPasswordForm(void);
// Game option functions
static void addGameOptions();
static void addChatBox (void);
static void addConsoleBox(void);
static void disableMultiButs (void);
static void processMultiopWidgets(UDWORD);
static void SendFireUp (void);
static void decideWRF (void);
static void closeColourChooser (void);
static void closeTeamChooser (void);
static void closePositionChooser (void);
static void closeAiChooser (void);
static void closeDifficultyChooser (void);
static bool SendColourRequest (UBYTE player, UBYTE col);
static bool SendPositionRequest (UBYTE player, UBYTE chosenPlayer);
static bool safeToUseColour (UDWORD player,UDWORD col);
static bool changeReadyStatus (UBYTE player, bool bReady);
static void stopJoining(void);
static int difficultyIcon(int difficulty);
// ////////////////////////////////////////////////////////////////////////////
// map previews..
static const char *difficultyList[] = { N_("Easy"), N_("Medium"), N_("Hard"), N_("Insane") };
static const int difficultyValue[] = { 1, 10, 15, 20 };
static struct
{
bool scavengers;
bool alliances;
bool teams;
bool power;
bool difficulty;
bool ai;
bool position;
bool bases;
} locked;
struct AIDATA
{
AIDATA() : assigned(0) {}
char name[MAX_LEN_AI_NAME];
char slo[MAX_LEN_AI_NAME];
char vlo[MAX_LEN_AI_NAME];
2011-01-23 13:30:57 -08:00
char js[MAX_LEN_AI_NAME];
char tip[255];
int assigned; ///< How many AIs have we assigned of this type
};
static std::vector<AIDATA> aidata;
/// Player name overrides
static char nameOverrides[MAX_PLAYERS][MAX_LEN_AI_NAME];
struct WzMultiButton : public W_BUTTON
{
WzMultiButton(WIDGET *parent) : W_BUTTON(parent) {}
void display(int xOffset, int yOffset);
Image imNormal;
Image imDown;
unsigned doHighlight;
unsigned tc;
};
const char *getAIName(int player)
{
if (NetPlay.players[player].ai >= 0)
{
return aidata[NetPlay.players[player].ai].name;
}
else
{
return "";
}
}
int getNextAIAssignment(const char *name)
{
int ai = matchAIbyName(name);
int match = aidata[ai].assigned;
for (int i = 0; i < game.maxPlayers; i++)
{
if (ai == NetPlay.players[i].ai)
{
if (match == 0)
{
aidata[ai].assigned++;
debug(LOG_SAVE, "wzscript assigned to player %d", i);
return i; // found right player
}
match--; // find next of this type
}
}
return AI_NOT_FOUND;
}
void loadMultiScripts()
{
bool defaultRules = true;
char aFileName[256];
char aPathName[256];
const char *ininame = challengeActive ? sRequestResult : aFileName;
const char *path = challengeActive ? "challenges/" : aPathName;
LEVEL_DATASET *psLevel = levFindDataSet(game.map, &game.hash);
ASSERT_OR_RETURN(, psLevel, "No level found for %s", game.map);
sstrcpy(aFileName, psLevel->apDataFiles[psLevel->game]);
aFileName[strlen(aFileName) - 4] = '\0';
sstrcpy(aPathName, aFileName);
sstrcat(aFileName, ".ini");
sstrcat(aPathName, "/");
// Reset assigned counter
std::vector<AIDATA>::iterator it;
for (it = aidata.begin(); it < aidata.end(); it++)
{
(*it).assigned = 0;
}
// Load map scripts
WzConfig ini(ininame, WzConfig::ReadOnly);
if (PHYSFS_exists(ininame))
{
debug(LOG_SAVE, "Loading map scripts");
ini.beginGroup("scripts");
if (ini.contains("extra"))
{
loadGlobalScript(path + ini.value("extra").toString());
}
if (ini.contains("rules"))
{
loadGlobalScript(path + ini.value("rules").toString());
defaultRules = false;
}
ini.endGroup();
}
// Load multiplayer rules
resForceBaseDir("messages/");
resLoadFile("SMSG", "multiplay.txt");
if (defaultRules)
{
debug(LOG_SAVE, "Loading default rules");
loadGlobalScript("multiplay/skirmish/rules.js");
}
// Backup data hashes, since AI and scavenger scripts aren't run on all clients.
uint32_t oldHash1 = DataHash[DATA_SCRIPT];
uint32_t oldHash2 = DataHash[DATA_SCRIPTVAL];
// Load AI players
resForceBaseDir("multiplay/skirmish/");
for (int i = 0; i < game.maxPlayers; i++)
{
if (NetPlay.players[i].ai < 0 && i == selectedPlayer)
{
NetPlay.players[i].ai = 0; // For autogames.
}
// The i == selectedPlayer hack is to enable autogames
2011-01-23 13:30:57 -08:00
if (bMultiPlayer && game.type == SKIRMISH && (!NetPlay.players[i].allocated || i == selectedPlayer)
&& NetPlay.players[i].ai >= 0 && myResponsibility(i))
{
if (PHYSFS_exists(ininame)) // challenge file may override AI
{
ini.beginGroup("player_" + QString::number(i + 1));
if (ini.contains("ai"))
{
QString val = ini.value("ai").toString();
ini.endGroup();
if (val.compare("null") == 0)
{
continue; // no AI
}
loadPlayerScript(val, i, NetPlay.players[i].difficulty);
NetPlay.players[i].ai = AI_CUSTOM;
continue;
}
ini.endGroup();
}
if (aidata[NetPlay.players[i].ai].slo[0] != '\0')
{
debug(LOG_SAVE, "Loading wzscript AI for player %d", i);
resLoadFile("SCRIPT", aidata[NetPlay.players[i].ai].slo);
resLoadFile("SCRIPTVAL", aidata[NetPlay.players[i].ai].vlo);
}
// autogames are to be implemented differently for qtscript, do not start for human players yet
if (!NetPlay.players[i].allocated && aidata[NetPlay.players[i].ai].js[0] != '\0')
2011-01-23 13:30:57 -08:00
{
debug(LOG_SAVE, "Loading javascript AI for player %d", i);
2011-02-06 09:19:05 -08:00
loadPlayerScript(QString("multiplay/skirmish/") + aidata[NetPlay.players[i].ai].js, i, NetPlay.players[i].difficulty);
2011-01-23 13:30:57 -08:00
}
}
}
// Load scavengers
2011-01-23 13:30:57 -08:00
if (game.scavengers && myResponsibility(scavengerPlayer()))
{
debug(LOG_SAVE, "Loading scavenger AI for player %d", scavengerPlayer());
2011-02-06 09:19:05 -08:00
loadPlayerScript("multiplay/script/scavfact.js", scavengerPlayer(), DIFFICULTY_EASY);
}
// Restore data hashes, since AI and scavenger scripts aren't run on all clients.
DataHash[DATA_SCRIPT] = oldHash1; // Not all players load the same AI scripts.
DataHash[DATA_SCRIPTVAL] = oldHash2;
// Reset resource path, otherwise things break down the line
resForceBaseDir("");
}
static int guessMapTilesetType(LEVEL_DATASET *psLevel)
{
unsigned t = 0, c = 0;
if (psLevel->psBaseData && psLevel->psBaseData->pName)
{
if (sscanf(psLevel->psBaseData->pName, "MULTI_CAM_%u", &c) != 1)
{
sscanf(psLevel->psBaseData->pName, "MULTI_T%u_C%u", &t, &c);
}
}
switch (c)
{
case 1:
return TILESET_ARIZONA;
break;
case 2:
return TILESET_URBAN;
break;
case 3:
return TILESET_ROCKIES;
break;
}
debug(LOG_MAP, "Could not guess map tileset, using ARIZONA.");
return TILESET_ARIZONA;
}
static void loadEmptyMapPreview()
{
// No map is available to preview, so improvise.
char *imageData = (char *)malloc(BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT * 3);
memset(imageData, 0, BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT * 3); //dunno about background color
int const ex = 100, ey = 100, bx = 5, by = 8;
for (unsigned n = 0; n < 125; ++n)
{
int sx = rand()%(ex - bx), sy = rand()%(ey - by);
char col[3] = {char(rand()%256), char(rand()%256), char(rand()%256)};
for (unsigned y = 0; y < by; ++y)
for (unsigned x = 0; x < bx; ++x)
if (("\2\1\261\11\6"[x]>>y & 1) == 1) // ?
memcpy(imageData + 3*(sx + x + BACKDROP_HACK_WIDTH*(sy + y)), col, 3);
}
// Slight hack to init array with a special value used to determine how many players on map
Vector2i playerpos[MAX_PLAYERS];
memset(playerpos, 0x77, sizeof(playerpos));
2013-02-10 14:27:26 -08:00
screen_enableMapPreview(ex, ey, playerpos);
screen_Upload(imageData);
free(imageData);
}
/// Loads the entire map just to show a picture of it
void loadMapPreview(bool hideInterface)
{
static char aFileName[256];
UDWORD fileSize;
char *pFileData = NULL;
LEVEL_DATASET *psLevel = NULL;
PIELIGHT plCliffL, plCliffH, plWater, plRoadL, plRoadH, plGroundL, plGroundH;
UDWORD x, y, height;
UBYTE col;
MAPTILE *psTile,*WTile;
UDWORD oursize;
Vector2i playerpos[MAX_PLAYERS]; // Will hold player positions
char *ptr = NULL, *imageData = NULL;
// absurd hack, since there is a problem with updating this crap piece of info, we're setting it to
// true by default for now, like it used to be
game.mapHasScavengers = true; // this is really the wrong place for it, but this is where it has to be
if(psMapTiles)
{
mapShutdown();
}
// load the terrain types
psLevel = levFindDataSet(game.map, &game.hash);
if (psLevel == NULL)
{
debug(LOG_INFO, "Could not find level dataset \"%s\" %s. We %s waiting for a download.", game.map, game.hash.toString().c_str(), NetPlay.players[selectedPlayer].needFile? "are":"aren't");
loadEmptyMapPreview();
return;
}
if (psLevel->realFileName == NULL)
{
2013-12-07 17:11:38 -08:00
debug(LOG_WZ, "Loading map preview: \"%s\" builtin t%d", psLevel->pName, psLevel->dataDir);
}
else
{
2013-12-07 17:11:38 -08:00
debug(LOG_WZ, "Loading map preview: \"%s\" in (%s)\"%s\" %s t%d", psLevel->pName, PHYSFS_getRealDir(psLevel->realFileName), psLevel->realFileName, psLevel->realFileHash.toString().c_str(), psLevel->dataDir);
}
rebuildSearchPath(psLevel->dataDir, false, psLevel->realFileName);
sstrcpy(aFileName, psLevel->apDataFiles[psLevel->game]);
aFileName[strlen(aFileName)-4] = '\0';
sstrcat(aFileName, "/ttypes.ttp");
pFileData = fileLoadBuffer;
if (!loadFileToBuffer(aFileName, pFileData, FILE_LOAD_BUFFER_SIZE, &fileSize))
{
debug(LOG_ERROR, "Failed to load terrain types file: [%s]", aFileName);
return;
}
if (pFileData)
{
if (!loadTerrainTypeMap(pFileData, fileSize))
{
debug(LOG_ERROR, "Failed to load terrain types");
return;
}
}
// load the map data
ptr = strrchr(aFileName, '/');
ASSERT_OR_RETURN(, ptr, "this string was supposed to contain a /");
strcpy(ptr, "/game.map");
if (!mapLoad(aFileName, true))
{
debug(LOG_ERROR, "Failed to load map");
return;
}
gwShutDown();
// set tileset colors
switch (guessMapTilesetType(psLevel))
{
case TILESET_ARIZONA:
plCliffL = WZCOL_TERC1_CLIFF_LOW;
plCliffH = WZCOL_TERC1_CLIFF_HIGH;
plWater = WZCOL_TERC1_WATER;
plRoadL = WZCOL_TERC1_ROAD_LOW;
plRoadH = WZCOL_TERC1_ROAD_HIGH;
plGroundL = WZCOL_TERC1_GROUND_LOW;
plGroundH = WZCOL_TERC1_GROUND_HIGH;
break;
case TILESET_URBAN:
plCliffL = WZCOL_TERC2_CLIFF_LOW;
plCliffH = WZCOL_TERC2_CLIFF_HIGH;
plWater = WZCOL_TERC2_WATER;
plRoadL = WZCOL_TERC2_ROAD_LOW;
plRoadH = WZCOL_TERC2_ROAD_HIGH;
plGroundL = WZCOL_TERC2_GROUND_LOW;
plGroundH = WZCOL_TERC2_GROUND_HIGH;
break;
case TILESET_ROCKIES:
plCliffL = WZCOL_TERC3_CLIFF_LOW;
plCliffH = WZCOL_TERC3_CLIFF_HIGH;
plWater = WZCOL_TERC3_WATER;
plRoadL = WZCOL_TERC3_ROAD_LOW;
plRoadH = WZCOL_TERC3_ROAD_HIGH;
plGroundL = WZCOL_TERC3_GROUND_LOW;
plGroundH = WZCOL_TERC3_GROUND_HIGH;
break;
}
oursize = sizeof(char) * BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT;
imageData = (char*)malloc(oursize * 3); // used for the texture
if( !imageData )
{
debug(LOG_FATAL,"Out of memory for texture!");
abort(); // should be a fatal error ?
return;
}
ptr = imageData;
memset(ptr, 0, sizeof(char) * BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT * 3); //dunno about background color
psTile = psMapTiles;
for (y = 0; y < mapHeight; y++)
{
WTile = psTile;
for (x = 0; x < mapWidth; x++)
{
char * const p = imageData + (3 * (y * BACKDROP_HACK_WIDTH + x));
height = WTile->height / ELEVATION_SCALE;
col = height;
switch (terrainType(WTile))
{
case TER_CLIFFFACE:
p[0] = plCliffL.byte.r + (plCliffH.byte.r-plCliffL.byte.r) * col / 256;
p[1] = plCliffL.byte.g + (plCliffH.byte.g-plCliffL.byte.g) * col / 256;
p[2] = plCliffL.byte.b + (plCliffH.byte.b-plCliffL.byte.b) * col / 256;
break;
case TER_WATER:
p[0] = plWater.byte.r;
p[1] = plWater.byte.g;
p[2] = plWater.byte.b;
break;
case TER_ROAD:
p[0] = plRoadL.byte.r + (plRoadH.byte.r-plRoadL.byte.r) * col / 256;
p[1] = plRoadL.byte.g + (plRoadH.byte.g-plRoadL.byte.g) * col / 256;
p[2] = plRoadL.byte.b + (plRoadH.byte.b-plRoadL.byte.b) * col / 256;
break;
default:
p[0] = plGroundL.byte.r + (plGroundH.byte.r-plGroundL.byte.r) * col / 256;
p[1] = plGroundL.byte.g + (plGroundH.byte.g-plGroundL.byte.g) * col / 256;
p[2] = plGroundL.byte.b + (plGroundH.byte.b-plGroundL.byte.b) * col / 256;
break;
}
WTile += 1;
}
psTile += mapWidth;
}
// Slight hack to init array with a special value used to determine how many players on map
memset(playerpos,0x77,sizeof(playerpos));
// color our texture with clancolors @ correct position
plotStructurePreview16(imageData, playerpos);
2013-02-10 14:27:26 -08:00
screen_enableMapPreview(mapWidth, mapHeight, playerpos);
screen_Upload(imageData);
free(imageData);
if (hideInterface)
{
hideTime = gameTime;
}
mapShutdown();
}
// ////////////////////////////////////////////////////////////////////////////
// helper func
int matchAIbyName(const char *name)
{
std::vector<AIDATA>::iterator it;
int i = 0;
if (name[0] == '\0')
{
return AI_CLOSED;
}
for (it = aidata.begin(); it < aidata.end(); it++, i++)
{
if (strncasecmp(name, (*it).name, MAX_LEN_AI_NAME) == 0)
{
return i;
}
}
return AI_NOT_FOUND;
}
void readAIs()
{
char basepath[PATH_MAX];
const char *sSearchPath = "multiplay/skirmish/";
char **i, **files;
aidata.clear();
strcpy(basepath, sSearchPath);
files = PHYSFS_enumerateFiles(basepath);
for (i = files; *i != NULL; ++i)
{
char path[PATH_MAX];
// See if this filename contains the extension we're looking for
if (!strstr(*i, ".ai"))
{
continue;
}
sstrcpy(path, basepath);
sstrcat(path, *i);
WzConfig aiconf(path, WzConfig::ReadOnly);
AIDATA ai;
aiconf.beginGroup("AI");
sstrcpy(ai.name, aiconf.value("name", "error").toString().toUtf8().constData());
sstrcpy(ai.slo, aiconf.value("slo", "").toString().toUtf8().constData());
sstrcpy(ai.vlo, aiconf.value("vlo", "").toString().toUtf8().constData());
sstrcpy(ai.js, aiconf.value("js", "").toString().toUtf8().constData());
sstrcpy(ai.tip, aiconf.value("tip", "Click to choose this AI").toString().toUtf8().constData());
if (strcmp(ai.name, "Nexus") == 0)
{
std::vector<AIDATA>::iterator it;
it = aidata.begin();
aidata.insert(it, ai);
}
else
{
aidata.push_back(ai);
}
}
PHYSFS_freeList(files);
}
//sets sWRFILE form game.map
static void decideWRF(void)
{
// try and load it from the maps directory first,
sstrcpy(aLevelName, MultiCustomMapsPath);
sstrcat(aLevelName, game.map);
sstrcat(aLevelName, ".wrf");
debug(LOG_WZ, "decideWRF: %s", aLevelName);
//if the file exists in the downloaded maps dir then use that one instead.
// FIXME: Try to incorporate this into physfs setup somehow for sane paths
if ( !PHYSFS_exists(aLevelName) )
{
sstrcpy(aLevelName, game.map); // doesn't exist, must be a predefined one.
}
}
// ////////////////////////////////////////////////////////////////////////////
// Connection Options Screen.
static bool OptionsInet(void) //internet options
{
psConScreen = new W_SCREEN;
W_FORMINIT sFormInit; //Connection Settings
sFormInit.formID = 0;
sFormInit.id = CON_SETTINGS;
sFormInit.style = WFORM_PLAIN;
sFormInit.x = CON_SETTINGSX;
sFormInit.y = CON_SETTINGSY;
sFormInit.width = CON_SETTINGSWIDTH;
sFormInit.height = CON_SETTINGSHEIGHT;
sFormInit.pDisplay = intDisplayFeBox;
widgAddForm(psConScreen, &sFormInit);
addMultiBut(psConScreen, CON_SETTINGS,CON_OK,CON_OKX,CON_OKY,MULTIOP_OKW,MULTIOP_OKH,
_("Accept Settings"),IMAGE_OK,IMAGE_OK,true);
addMultiBut(psConScreen, CON_SETTINGS,CON_IP_CANCEL,CON_OKX+MULTIOP_OKW+10,CON_OKY,MULTIOP_OKW,MULTIOP_OKH,
_("Cancel"),IMAGE_NO,IMAGE_NO,true);
//label.
W_LABINIT sLabInit;
sLabInit.formID = CON_SETTINGS;
sLabInit.id = CON_SETTINGS_LABEL;
sLabInit.style = WLAB_ALIGNCENTRE;
sLabInit.x = 0;
sLabInit.y = 10;
sLabInit.width = CON_SETTINGSWIDTH;
sLabInit.height = 20;
sLabInit.pText = _("IP Address or Machine Name");
widgAddLabel(psConScreen, &sLabInit);
W_EDBINIT sEdInit; // address
sEdInit.formID = CON_SETTINGS;
sEdInit.id = CON_IP;
sEdInit.x = CON_IPX;
sEdInit.y = CON_IPY;
sEdInit.width = CON_NAMEBOXWIDTH;
sEdInit.height = CON_NAMEBOXHEIGHT;
sEdInit.pText = ""; //_("IP Address or Machine Name");
sEdInit.pBoxDisplay = intDisplayEditBox;
if (!widgAddEditBox(psConScreen, &sEdInit))
{
return false;
}
// auto click in the text box
W_CONTEXT sContext;
sContext.xOffset = 0;
sContext.yOffset = 0;
sContext.mx = 0;
sContext.my = 0;
widgGetFromID(psConScreen, CON_IP)->clicked(&sContext);
SettingsUp = true;
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// Draw the connections screen.
bool startConnectionScreen(void)
{
addBackdrop(); //background
addTopForm(); // logo
addBottomForm();
SettingsUp = false;
InitialProto = 0;
safeSearch = false;
// don't pretend we are running a network game. Really do it!
NetPlay.bComms = true; // use network = true
addSideText(FRONTEND_SIDETEXT, FRONTEND_SIDEX, FRONTEND_SIDEY,_("CONNECTION"));
addMultiBut(psWScreen,FRONTEND_BOTFORM,CON_CANCEL,10,10,MULTIOP_OKW,MULTIOP_OKH,
_("Return To Previous Screen"), IMAGE_RETURN, IMAGE_RETURN_HI, IMAGE_RETURN_HI); // goback buttpn levels
addTextButton(CON_TYPESID_START+0,FRONTEND_POS2X,FRONTEND_POS2Y, _("Lobby"), WBUT_TXTCENTRE);
addTextButton(CON_TYPESID_START+1,FRONTEND_POS3X,FRONTEND_POS3Y, _("IP"), WBUT_TXTCENTRE);
return true;
}
void runConnectionScreen(void)
{
static char addr[128];
W_SCREEN *curScreen = SettingsUp? psConScreen : psWScreen;
WidgetTriggers const &triggers = widgRunScreen(curScreen);
unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu.
switch(id)
{
case CON_CANCEL: //cancel
changeTitleMode(MULTI);
bMultiPlayer = false;
bMultiMessages = false;
break;
case CON_TYPESID_START+0: // Lobby button
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (LobbyError != ERROR_INVALID)
{
setLobbyError(ERROR_NOERROR);
}
changeTitleMode(GAMEFIND);
break;
case CON_TYPESID_START+1: // IP button
OptionsInet();
break;
case CON_OK:
sstrcpy(addr, widgGetString(psConScreen, CON_IP));
if (addr[0] == '\0')
{
sstrcpy(addr, "127.0.0.1"); // Default to localhost.
}
if(SettingsUp == true)
{
delete psConScreen;
psConScreen = NULL;
SettingsUp = false;
}
joinGame(addr, 0);
break;
case CON_IP_CANCEL:
if (SettingsUp == true)
{
delete psConScreen;
psConScreen = NULL;
SettingsUp = false;
}
break;
}
widgDisplayScreen(psWScreen); // show the widgets currently running
if(SettingsUp == true)
{
widgDisplayScreen(psConScreen); // show the widgets currently running
}
if (CancelPressed())
{
changeTitleMode(MULTI);
}
}
// ////////////////////////////////////////////////////////////////////////
// Lobby error reading
LOBBY_ERROR_TYPES getLobbyError(void)
{
return LobbyError;
}
void setLobbyError (LOBBY_ERROR_TYPES error_type)
{
LobbyError = error_type;
if (LobbyError <= ERROR_FULL)
{
disableLobbyRefresh = false;
}
else
{
disableLobbyRefresh = true;
}
}
/**
* Try connecting to the given host, show
* the multiplayer screen or a error.
*/
bool joinGame(const char* host, uint32_t port)
{
PLAYERSTATS playerStats;
if(ingame.localJoiningInProgress)
{
return false;
}
if (!NETjoinGame(host, port, (char*)sPlayer)) // join
{
switch (getLobbyError())
{
case ERROR_HOSTDROPPED:
setLobbyError(ERROR_NOERROR);
break;
case ERROR_WRONGPASSWORD:
{
// Change to GAMEFIND, screen with a password dialog.
changeTitleMode(GAMEFIND);
showPasswordForm();
WidgetTriggers const &triggers = widgRunScreen(psWScreen);
unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu.
if (id != CON_PASSWORD) // Run the current set of widgets
{
return false;
}
NETsetGamePassword(widgGetString(psWScreen, CON_PASSWORD));
break;
}
default:
break;
}
// Hide a (maybe shown) password form.
hidePasswordForm();
// Change to GAMEFIND, screen.
changeTitleMode(GAMEFIND);
// Shows the lobby error.
addGames();
addConsoleBox();
return false;
}
ingame.localJoiningInProgress = true;
loadMultiStats(sPlayer,&playerStats);
setMultiStats(selectedPlayer, playerStats, false);
setMultiStats(selectedPlayer, playerStats, true);
changeTitleMode(MULTIOPTION);
if (war_getMPcolour() >= 0)
{
SendColourRequest(selectedPlayer, war_getMPcolour());
}
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// Game Chooser Screen.
static void addGames(void)
{
int i, gcount = 0, added = 0;
static const char *wrongVersionTip = "Your version of Warzone is incompatible with this game.";
static const char *badModTip = "Your loaded mods are incompatible with this game. (Check mods/autoload/?)";
//count games to see if need two columns.
for (i=0; i < MaxGames; i++)
{
if (NetPlay.games[i].desc.dwSize !=0)
{
gcount++;
}
}
W_BUTINIT sButInit;
sButInit.formID = FRONTEND_BOTFORM;
sButInit.width = GAMES_GAMEWIDTH;
sButInit.height = GAMES_GAMEHEIGHT;
sButInit.pDisplay = displayRemoteGame;
// we want the old games deleted, and only list games when we should
if (getLobbyError() || !gcount)
{
for(i = 0; i<MaxGames; i++)
{
widgDelete(psWScreen, GAMES_GAMESTART+i); // remove old widget
}
gcount = 0;
}
// in case they refresh, and a game becomes available.
widgDelete(psWScreen, FRONTEND_NOGAMESAVAILABLE);
// only have to do this if we have any games available.
if (!getLobbyError() && gcount)
{
for (i=0; i<MaxGames; i++) // draw games
{
widgDelete(psWScreen, GAMES_GAMESTART+i); // remove old icon.
if (NetPlay.games[i].desc.dwSize !=0 )
{ // either display all games, or games that are the client's specific version
if (toggleFilter)
{
if ((NetPlay.games[i].game_version_major != NETGetMajorVersion()) || (NetPlay.games[i].game_version_minor != NETGetMinorVersion()))
continue;
}
added++;
sButInit.id = GAMES_GAMESTART+i;
sButInit.x = 20;
sButInit.y = (UWORD)(45+((5+GAMES_GAMEHEIGHT)*i) );
// display the correct tooltip message.
if (!NETisCorrectVersion(NetPlay.games[i].game_version_major, NetPlay.games[i].game_version_minor))
{
sButInit.pTip = wrongVersionTip;
}
else if (strcmp(NetPlay.games[i].modlist,getModList()) != 0)
{
sButInit.pTip = badModTip;
}
else
{
std::string flags;
if (NetPlay.games[i].privateGame)
{
flags += " "; flags += _("[Password required]");
}
if (NetPlay.games[i].limits & NO_TANKS)
{
flags += " "; flags += _("[No Tanks]");
}
if (NetPlay.games[i].limits & NO_BORGS)
{
flags += " "; flags += _("[No Cyborgs]");
}
if (NetPlay.games[i].limits & NO_VTOLS)
{
flags += " "; flags += _("[No VTOLs]");
}
if (flags.empty())
{
ssprintf(tooltipbuffer[i], _("Hosted by %s"), NetPlay.games[i].hostname);
}
else
{
ssprintf(tooltipbuffer[i], _("Hosted by %s —%s"), NetPlay.games[i].hostname, flags.c_str());
}
sButInit.pTip = tooltipbuffer[i];
}
sButInit.UserData = i;
2011-04-25 12:07:08 -07:00
widgAddButton(psWScreen, &sButInit);
}
}
if (!added)
{
sButInit = W_BUTINIT();
sButInit.formID = FRONTEND_BOTFORM;
sButInit.id = FRONTEND_NOGAMESAVAILABLE;
sButInit.x = 70;
sButInit.y = 378;
sButInit.style = WBUT_TXTCENTRE;
sButInit.width = FRONTEND_BUTWIDTH;
sButInit.UserData = 0; // store disable state
sButInit.height = FRONTEND_BUTHEIGHT;
sButInit.pDisplay = displayTextOption;
sButInit.FontID = font_large;
sButInit.pText = _("Can't find any games for your version.");
widgAddButton(psWScreen, &sButInit);
}
}
else
{
// display lobby message based on results.
// This is a 'button', not text so it can be hilighted/centered.
const char *txt;
W_BUTINIT sButInit;
switch (getLobbyError())
{
case ERROR_NOERROR:
txt = _("No games are available");
break;
case ERROR_FULL:
txt = _("Game is full");
break;
case ERROR_KICKED:
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
case ERROR_INVALID:
txt = _("You were kicked!");
break;
case ERROR_WRONGVERSION:
txt = _("Wrong Game Version!");
break;
case ERROR_WRONGDATA:
txt = _("You have an incompatible mod.");
break;
// AFAIK, the only way this can really happy is if the Host's file is named wrong, or a client side error.
case ERROR_UNKNOWNFILEISSUE:
txt = _("Host couldn't send file?");
debug(LOG_POPUP, "Warzone couldn't complete a file request.\n\nPossibly, Host's file is incorrect. Check your logs for more details.");
break;
case ERROR_WRONGPASSWORD:
txt = _("Incorrect Password!");
break;
case ERROR_HOSTDROPPED:
txt = _("Host has dropped connection!");
break;
case ERROR_CONNECTION:
default:
txt = _("Connection Error");
break;
}
// delete old widget if necessary
widgDelete(psWScreen,FRONTEND_NOGAMESAVAILABLE);
sButInit = W_BUTINIT();
sButInit.formID = FRONTEND_BOTFORM;
sButInit.id = FRONTEND_NOGAMESAVAILABLE;
sButInit.x = 70;
sButInit.y = 50;
sButInit.style = WBUT_TXTCENTRE;
sButInit.width = FRONTEND_BUTWIDTH;
sButInit.UserData = 0; // store disable state
sButInit.height = FRONTEND_BUTHEIGHT;
sButInit.pDisplay = displayTextOption;
sButInit.FontID = font_large;
sButInit.pText = txt;
widgAddButton(psWScreen, &sButInit);
}
}
static void removeGames(void)
{
int i;
for (i = 0; i<MaxGames; i++)
{
widgDelete(psWScreen, GAMES_GAMESTART+i); // remove old widget
}
widgDelete(psWScreen,FRONTEND_NOGAMESAVAILABLE);
}
void runGameFind(void )
{
static UDWORD lastupdate=0;
static char game_password[StringSize];
if (lastupdate > gameTime) lastupdate = 0;
if (gameTime-lastupdate > 6000 && !EnablePasswordPrompt)
{
lastupdate = gameTime;
if(safeSearch)
{
if (!NETfindGame()) // find games synchronously
{
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
}
}
addGames(); //redraw list
addConsoleBox();
}
WidgetTriggers const &triggers = widgRunScreen(psWScreen);
unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu.
if(id == CON_CANCEL) // ok
{
changeTitleMode(PROTOCOL);
}
if(id == MULTIOP_REFRESH || id == MULTIOP_FILTER_TOGGLE)
{
if (id == MULTIOP_FILTER_TOGGLE)
{
toggleFilter = !toggleFilter;
toggleFilter ? widgSetButtonState(psWScreen, MULTIOP_FILTER_TOGGLE, WBUT_CLICKLOCK) : widgSetButtonState(psWScreen, MULTIOP_FILTER_TOGGLE, 0);
}
else
{
widgSetButtonState(psWScreen, MULTIOP_FILTER_TOGGLE, 0);
}
ingame.localOptionsReceived = true;
if (!NETfindGame()) // find games synchronously
{
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
}
addGames(); //redraw list.
addConsoleBox();
}
if (id == CON_PASSWORD)
{
sstrcpy(game_password, widgGetString(psWScreen, CON_PASSWORD));
NETsetGamePassword(game_password);
}
// below is when they hit a game box to connect to--ideally this would be where
// we would want a modal password entry box.
if (id >= GAMES_GAMESTART && id <= GAMES_GAMEEND)
{
gameNumber = id - GAMES_GAMESTART;
if (NetPlay.games[gameNumber].privateGame)
{
showPasswordForm();
}
else
{
ingame.localOptionsReceived = false; // note, we are awaiting options
sstrcpy(game.name, NetPlay.games[gameNumber].name); // store name
joinGame(NetPlay.games[gameNumber].desc.host, 0);
}
}
else if (id == CON_PASSWORDYES)
{
ingame.localOptionsReceived = false; // note, we are awaiting options
sstrcpy(game.name, NetPlay.games[gameNumber].name); // store name
joinGame(NetPlay.games[gameNumber].desc.host, 0);
}
else if (id == CON_PASSWORDNO)
{
hidePasswordForm();
}
widgDisplayScreen(psWScreen); // show the widgets currently running
if(safeSearch)
{
iV_SetFont(font_large);
iV_DrawText(_("Searching"), D_W+260, D_H+460);
}
if (CancelPressed())
{
changeTitleMode(PROTOCOL);
}
// console box handling
iV_SetFont(font_small);
if(widgGetFromID(psWScreen, MULTIOP_CONSOLEBOX))
{
while(getNumberConsoleMessages() > getConsoleLineInfo())
{
removeTopConsoleMessage();
}
updateConsoleMessages();
}
displayConsoleMessages();
}
// This is what starts the lobby screen
void startGameFind(void)
{
addBackdrop(); //background image
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
// draws the background of the games listed
IntFormAnimated *botForm = new IntFormAnimated(parent);
botForm->id = FRONTEND_BOTFORM;
botForm->setGeometry(MULTIOP_OPTIONSX, MULTIOP_OPTIONSY, MULTIOP_CHATBOXW, 415); // FIXME: Add box at bottom for server messages
addSideText(FRONTEND_SIDETEXT, MULTIOP_OPTIONSX-3, MULTIOP_OPTIONSY,_("GAMES"));
// cancel
addMultiBut(psWScreen,FRONTEND_BOTFORM,CON_CANCEL,10,5,MULTIOP_OKW,MULTIOP_OKH,_("Return To Previous Screen"),
IMAGE_RETURN, IMAGE_RETURN_HI, IMAGE_RETURN_HI);
//refresh
addMultiBut(psWScreen, FRONTEND_BOTFORM, MULTIOP_REFRESH, MULTIOP_CHATBOXW-MULTIOP_OKW-5, 5, MULTIOP_OKW, MULTIOP_OKH,
_("Refresh Games List"), IMAGE_RELOAD_HI, IMAGE_RELOAD, IMAGE_RELOAD);
//filter toggle
addMultiBut(psWScreen, FRONTEND_BOTFORM, MULTIOP_FILTER_TOGGLE, MULTIOP_CHATBOXW-MULTIOP_OKW-45, 5, MULTIOP_OKW, MULTIOP_OKH,
_("Filter Games List"),IMAGE_FILTER, IMAGE_FILTER_ON, IMAGE_FILTER_ON);
if (safeSearch || disableLobbyRefresh)
{
widgHide(psWScreen, MULTIOP_REFRESH);
2013-10-31 19:43:00 -07:00
widgHide(psWScreen, MULTIOP_FILTER_TOGGLE);
}
if (!NETfindGame())
{
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
}
addGames(); // now add games.
addConsoleBox();
displayConsoleMessages();
// Password stuff. Hidden by default.
// draws the background of the password box
IntFormAnimated *passwordForm = new IntFormAnimated(parent);
passwordForm->id = FRONTEND_PASSWORDFORM;
passwordForm->setGeometry(FRONTEND_BOTFORMX, 160, FRONTEND_TOPFORMW, FRONTEND_TOPFORMH - 40);
// password label.
W_LABEL *enterPasswordLabel = new W_LABEL(passwordForm);
enterPasswordLabel->setTextAlignment(WLAB_ALIGNCENTRE);
enterPasswordLabel->setGeometry(130, 0, 280, 40);
enterPasswordLabel->setFont(font_large, WZCOL_FORM_TEXT);
enterPasswordLabel->setString(_("Enter Password:"));
// and finally draw the password entry box
W_EDITBOX *passwordBox = new W_EDITBOX(passwordForm);
passwordBox->id = CON_PASSWORD;
passwordBox->setGeometry(130, 40, 280, 20);
passwordBox->setBoxColours(WZCOL_MENU_BORDER, WZCOL_MENU_BORDER, WZCOL_MENU_BACKGROUND);
W_BUTTON *buttonYes = new W_BUTTON(passwordForm);
buttonYes->id = CON_PASSWORDYES;
buttonYes->setImages(Image(FrontImages, IMAGE_OK), Image(FrontImages, IMAGE_OK), getFrontHighlightImage(Image(FrontImages, IMAGE_OK)));
buttonYes->move(180, 65);
buttonYes->setTip(_("OK"));
W_BUTTON *buttonNo = new W_BUTTON(passwordForm);
buttonNo->id = CON_PASSWORDNO;
buttonNo->setImages(Image(FrontImages, IMAGE_NO), Image(FrontImages, IMAGE_NO), getFrontHighlightImage(Image(FrontImages, IMAGE_NO)));
buttonNo->move(230, 65);
buttonNo->setTip(_("Cancel"));
passwordForm->hide();
EnablePasswordPrompt = false;
}
static void hidePasswordForm(void)
{
EnablePasswordPrompt = false;
if (widgGetFromID(psWScreen, FRONTEND_PASSWORDFORM)) widgHide(psWScreen, FRONTEND_PASSWORDFORM);
widgReveal(psWScreen, FRONTEND_SIDETEXT);
widgReveal(psWScreen, FRONTEND_BOTFORM);
widgReveal(psWScreen, CON_CANCEL);
if (!safeSearch && (!disableLobbyRefresh))
{
if (widgGetFromID(psWScreen, MULTIOP_REFRESH)) widgReveal(psWScreen, MULTIOP_REFRESH);
2013-10-31 19:43:00 -07:00
if (widgGetFromID(psWScreen, MULTIOP_FILTER_TOGGLE)) widgReveal(psWScreen,MULTIOP_FILTER_TOGGLE);
}
addGames();
addConsoleBox();
}
static void showPasswordForm(void)
{
W_CONTEXT sContext;
EnablePasswordPrompt = true;
widgHide(psWScreen, FRONTEND_SIDETEXT);
widgHide(psWScreen, FRONTEND_BOTFORM);
widgHide(psWScreen, CON_CANCEL);
widgHide(psWScreen, MULTIOP_REFRESH);
2013-10-31 19:43:00 -07:00
widgHide(psWScreen, MULTIOP_FILTER_TOGGLE);
removeGames();
widgReveal(psWScreen, FRONTEND_PASSWORDFORM);
// auto click in the password box
sContext.xOffset = 0;
sContext.yOffset = 0;
sContext.mx = 0;
sContext.my = 0;
widgGetFromID(psWScreen, CON_PASSWORD)->clicked(&sContext);
}
// ////////////////////////////////////////////////////////////////////////////
// Game Options Screen.
// ////////////////////////////////////////////////////////////////////////////
MultibuttonWidget::MultibuttonWidget(WIDGET* parent, int value)
: W_FORM(parent)
, label(nullptr)
, mapper(new QSignalMapper(this))
, currentValue_(value)
, disabled(false)
, gap_(3)
, lockCurrent(false)
{
connect(mapper, SIGNAL(mapped(int)), this, SLOT(choose(int)));
}
void MultibuttonWidget::display(int xOffset, int yOffset)
{
iV_ShadowBox(xOffset + x(), yOffset + y(), xOffset + x() + width() - 1, yOffset + y() + height() - 1, 0, WZCOL_MENU_BORDER, WZCOL_MENU_BORDER, WZCOL_MENU_BACKGROUND);
}
void MultibuttonWidget::geometryChanged()
{
int s = width() - gap_;
for (std::vector<std::pair<W_BUTTON *, int> >::const_reverse_iterator i = buttons.rbegin(); i != buttons.rend(); ++i)
{
i->first->move(s - i->first->width(), (height() - i->first->height())/2);
s -= i->first->width() + gap_;
}
if (label != nullptr)
{
label->setGeometry(gap_, 0, s - gap_, height());
}
}
void MultibuttonWidget::setLabel(char const *text)
{
delete label;
label = new W_LABEL(this);
label->setString(text);
geometryChanged();
}
void MultibuttonWidget::addButton(int value, Image image, Image imageDown, char const *tip)
{
W_BUTTON *button = new W_BUTTON(this);
button->setImages(image, imageDown, getFrontHighlightImage(image));
button->setTip(tip);
button->setState(value == currentValue_ && lockCurrent? WBUT_LOCK : disabled? WBUT_DISABLE : 0);
buttons.push_back(std::make_pair(button, value));
mapper->setMapping(button, value);
connect(button, SIGNAL(clicked()), mapper, SLOT(map()));
geometryChanged();
}
void MultibuttonWidget::enable(bool enabled)
{
if (!enabled == disabled)
{
return;
}
disabled = !enabled;
stateChanged();
}
void MultibuttonWidget::setGap(int gap)
{
if (gap == gap_)
{
return;
}
gap_ = gap;
geometryChanged();
}
void MultibuttonWidget::choose(int value)
{
if (value == currentValue_ && lockCurrent)
{
return;
}
currentValue_ = value;
stateChanged();
emit chosen(currentValue_);
screenPointer->setReturn(this);
}
void MultibuttonWidget::stateChanged()
{
for (std::vector<std::pair<W_BUTTON *, int> >::const_iterator i = buttons.begin(); i != buttons.end(); ++i)
{
i->first->setState(i->second == currentValue_ && lockCurrent? WBUT_LOCK : disabled? WBUT_DISABLE : 0);
}
}
MultichoiceWidget::MultichoiceWidget(WIDGET *parent, int value)
: MultibuttonWidget(parent, value)
{
lockCurrent = true;
}
static void addBlueForm(UDWORD parent,UDWORD id, const char *txt,UDWORD x,UDWORD y,UDWORD w,UDWORD h)
{
W_FORMINIT sFormInit; // draw options box.
sFormInit.formID= parent;
sFormInit.id = id;
sFormInit.x =(UWORD) x;
sFormInit.y =(UWORD) y;
sFormInit.style = WFORM_PLAIN;
sFormInit.width = (UWORD)w;//190;
sFormInit.height= (UWORD)h;//27;
sFormInit.pDisplay = intDisplayFeBox;
widgAddForm(psWScreen, &sFormInit);
if(strlen(txt)>0)
{
W_LABINIT sLabInit;
sLabInit.formID = id;
sLabInit.id = id+1;
sLabInit.x = 3;
sLabInit.y = 4;
sLabInit.width = 80;
sLabInit.height = 20;
sLabInit.pText = txt;
widgAddLabel(psWScreen, &sLabInit);
}
return;
}
struct LimitIcon
{
char const *stat;
char const *desc;
int icon;
};
static const LimitIcon limitIcons[] =
{
{"A0LightFactory", N_("Tanks disabled!!"), IMAGE_NO_TANK},
{"A0CyborgFactory", N_("Cyborgs disabled."), IMAGE_NO_CYBORG},
{"A0VTolFactory1", N_("VTOLs disabled."), IMAGE_NO_VTOL},
{"A0Sat-linkCentre", N_("Satellite Uplink disabled."), IMAGE_NO_UPLINK},
{"A0LasSatCommand", N_("Laser Satellite disabled."), IMAGE_NO_LASSAT},
};
void updateLimitFlags()
{
unsigned i;
unsigned flags = 0;
if (!ingame.bHostSetup)
{
return; // The host works out the flags.
}
for (i = 0; i < ARRAY_SIZE(limitIcons); ++i)
{
int stat = getStructStatFromName(limitIcons[i].stat);
bool disabled = asStructLimits[0] != NULL && stat >= 0 && asStructLimits[0][stat].limit == 0;
flags |= disabled<<i;
}
ingame.flags = flags;
}
// need to check for side effects.
static void addGameOptions()
{
widgDelete(psWScreen,MULTIOP_OPTIONS); // clear options list
widgDelete(psWScreen,FRONTEND_SIDETEXT3); // del text..
iV_SetFont(font_regular);
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
// draw options box.
IntFormAnimated *optionsForm = new IntFormAnimated(parent, false);
optionsForm->id = MULTIOP_OPTIONS;
optionsForm->setGeometry(MULTIOP_OPTIONSX, MULTIOP_OPTIONSY, MULTIOP_OPTIONSW, MULTIOP_OPTIONSH);
addSideText(FRONTEND_SIDETEXT3, MULTIOP_OPTIONSX-3 , MULTIOP_OPTIONSY,_("OPTIONS"));
// game name box
if (!NetPlay.bComms)
{
addMultiEditBox(MULTIOP_OPTIONS, MULTIOP_GNAME, MCOL0, MROW2, _("Game Name"),
challengeActive ? game.name : _("One-Player Skirmish"), IMAGE_EDIT_GAME,
IMAGE_EDIT_GAME_HI, MULTIOP_GNAME_ICON);
// disable for one-player skirmish
widgSetButtonState(psWScreen, MULTIOP_GNAME, WEDBS_DISABLE);
}
else
{
addMultiEditBox(MULTIOP_OPTIONS, MULTIOP_GNAME, MCOL0, MROW2, _("Select Game Name"), game.name, IMAGE_EDIT_GAME, IMAGE_EDIT_GAME_HI, MULTIOP_GNAME_ICON);
}
widgSetButtonState(psWScreen, MULTIOP_GNAME_ICON, WBUT_DISABLE);
// map chooser
addBlueForm(MULTIOP_OPTIONS, MULTIOP_MAP, game.map, MCOL0, MROW3, MULTIOP_BLUEFORMW, 29);
addMultiBut(psWScreen, MULTIOP_MAP, MULTIOP_MAP_ICON, MCOL4, 2, 20, MULTIOP_BUTH, _("Select Map"), IMAGE_EDIT_MAP, IMAGE_EDIT_MAP_HI, true);
addMultiBut(psWScreen, MULTIOP_MAP, MULTIOP_MAP_MOD, MCOL3+11, 10, 12, 12, _("Map-Mod!"), IMAGE_LAMP_RED, IMAGE_LAMP_AMBER, false);
if (!game.isMapMod)
{
widgHide(psWScreen, MULTIOP_MAP_MOD);
}
// disable for challenges
if (challengeActive)
{
widgSetButtonState(psWScreen, MULTIOP_MAP_ICON, WBUT_DISABLE);
}
// password box
if (ingame.bHostSetup && NetPlay.bComms)
{
addMultiEditBox(MULTIOP_OPTIONS, MULTIOP_PASSWORD_EDIT, MCOL0, MROW4, _("Click to set Password"), NetPlay.gamePassword, IMAGE_UNLOCK_BLUE, IMAGE_LOCK_BLUE, MULTIOP_PASSWORD_BUT);
if (NetPlay.GamePassworded)
{
widgSetButtonState(psWScreen, MULTIOP_PASSWORD_BUT, WBUT_CLICKLOCK);
widgSetButtonState(psWScreen, MULTIOP_PASSWORD_EDIT, WEDBS_DISABLE);
}
}
//just display the game options.
addMultiEditBox(MULTIOP_OPTIONS, MULTIOP_PNAME, MCOL0, MROW1, _("Select Player Name"), (char*) sPlayer, IMAGE_EDIT_PLAYER, IMAGE_EDIT_PLAYER_HI, MULTIOP_PNAME_ICON);
ListWidget *optionsList = new ListWidget(optionsForm);
optionsList->setChildSize(MULTIOP_BLUEFORMW, 29);
optionsList->setChildSpacing(2, 2);
optionsList->setGeometry(MCOL0, MROW5, MULTIOP_BLUEFORMW, optionsForm->height() - MROW5);
MultichoiceWidget *scavengerChoice = new MultichoiceWidget(optionsList, game.scavengers);
scavengerChoice->id = MULTIOP_GAMETYPE;
scavengerChoice->setLabel(_("Scavengers"));
if (game.mapHasScavengers)
{
scavengerChoice->addButton(true, Image(FrontImages, IMAGE_SCAVENGERS_ON), Image(FrontImages, IMAGE_SCAVENGERS_ON_HI), _("Scavengers"));
}
scavengerChoice->addButton(false, Image(FrontImages, IMAGE_SCAVENGERS_OFF), Image(FrontImages, IMAGE_SCAVENGERS_OFF_HI), _("No Scavengers"));
scavengerChoice->enable(!locked.scavengers);
optionsList->addWidgetToLayout(scavengerChoice);
MultichoiceWidget *allianceChoice = new MultichoiceWidget(optionsList, game.alliance);
allianceChoice->id = MULTIOP_ALLIANCES;
allianceChoice->setLabel(_("Alliances"));
allianceChoice->addButton(NO_ALLIANCES, Image(FrontImages, IMAGE_NOALLI), Image(FrontImages, IMAGE_NOALLI_HI), _("No Alliances"));
allianceChoice->addButton(ALLIANCES, Image(FrontImages, IMAGE_ALLI), Image(FrontImages, IMAGE_ALLI_HI), _("Allow Alliances"));
allianceChoice->addButton(ALLIANCES_UNSHARED, Image(FrontImages, IMAGE_ALLI_UNSHARED), Image(FrontImages, IMAGE_ALLI_UNSHARED_HI), _("Locked Teams, No Shared Research"));
allianceChoice->addButton(ALLIANCES_TEAMS, Image(FrontImages, IMAGE_ALLI_TEAMS), Image(FrontImages, IMAGE_ALLI_TEAMS_HI), _("Locked Teams"));
allianceChoice->enable(!locked.alliances);
optionsList->addWidgetToLayout(allianceChoice);
MultichoiceWidget *powerChoice = new MultichoiceWidget(optionsList, game.power);
powerChoice->id = MULTIOP_POWER;
powerChoice->setLabel(_("Power"));
powerChoice->addButton(LEV_LOW, Image(FrontImages, IMAGE_POWLO), Image(FrontImages, IMAGE_POWLO_HI), _("Low Power Levels"));
powerChoice->addButton(LEV_MED, Image(FrontImages, IMAGE_POWMED), Image(FrontImages, IMAGE_POWMED_HI), _("Medium Power Levels"));
powerChoice->addButton(LEV_HI, Image(FrontImages, IMAGE_POWHI), Image(FrontImages, IMAGE_POWHI_HI), _("High Power Levels"));
powerChoice->enable(!locked.power);
optionsList->addWidgetToLayout(powerChoice);
MultichoiceWidget *baseTypeChoice = new MultichoiceWidget(optionsList, game.base);
baseTypeChoice->id = MULTIOP_BASETYPE;
baseTypeChoice->setLabel(_("Base"));
baseTypeChoice->addButton(CAMP_CLEAN, Image(FrontImages, IMAGE_NOBASE), Image(FrontImages, IMAGE_NOBASE_HI), _("Start with No Bases"));
baseTypeChoice->addButton(CAMP_BASE, Image(FrontImages, IMAGE_SBASE), Image(FrontImages, IMAGE_SBASE_HI), _("Start with Bases"));
baseTypeChoice->addButton(CAMP_WALLS, Image(FrontImages, IMAGE_LBASE), Image(FrontImages, IMAGE_LBASE_HI), _("Start with Advanced Bases"));
baseTypeChoice->enable(!locked.bases);
optionsList->addWidgetToLayout(baseTypeChoice);
MultibuttonWidget *mapPreviewButton = new MultibuttonWidget(optionsList);
mapPreviewButton->id = MULTIOP_MAP_PREVIEW;
mapPreviewButton->setLabel(_("Map Preview"));
mapPreviewButton->addButton(0, Image(FrontImages, IMAGE_FOG_OFF), Image(FrontImages, IMAGE_FOG_OFF_HI), _("Click to see Map"));
optionsList->addWidgetToLayout(mapPreviewButton);
if (ingame.bHostSetup)
{
MultibuttonWidget *structLimitsButton = new MultibuttonWidget(optionsList);
structLimitsButton->id = MULTIOP_STRUCTLIMITS;
structLimitsButton->setLabel(challengeActive ? _("Show Structure Limits") : _("Set Structure Limits"));
structLimitsButton->addButton(0, Image(FrontImages, IMAGE_SLIM), Image(FrontImages, IMAGE_SLIM_HI), challengeActive ? _("Show Structure Limits") : _("Set Structure Limits"));
optionsList->addWidgetToLayout(structLimitsButton);
}
if (ingame.bHostSetup && !bHosted && !challengeActive)
{
MultibuttonWidget *hostButton = new MultibuttonWidget(optionsList);
hostButton->id = MULTIOP_HOST;
hostButton->setLabel(_("Start Hosting Game"));
hostButton->addButton(0, Image(FrontImages, IMAGE_HOST), Image(FrontImages, IMAGE_HOST_HI), _("Start Hosting Game"));
optionsList->addWidgetToLayout(hostButton);
}
// cancel
addMultiBut(psWScreen,MULTIOP_OPTIONS,CON_CANCEL,
MULTIOP_CANCELX,MULTIOP_CANCELY,
iV_GetImageWidth(FrontImages,IMAGE_RETURN),
iV_GetImageHeight(FrontImages,IMAGE_RETURN),
_("Return To Previous Screen"), IMAGE_RETURN, IMAGE_RETURN_HI, IMAGE_RETURN_HI);
// Add any relevant factory disabled icons.
updateLimitFlags();
int y = 2;
bool skip = false;
for (int i = 0; i < ARRAY_SIZE(limitIcons); ++i)
{
if ((ingame.flags & 1<<i) != 0)
{
if (!skip)
{ // only add this once.
addBlueForm(MULTIOP_OPTIONS, MULTIOP_NO_SOMETHING, "", MULTIOP_HOSTX, MULTIOP_NO_SOMETHINGY, 41, 152);
}
addMultiBut(psWScreen, MULTIOP_NO_SOMETHING, MULTIOP_NO_SOMETHINGY + i, MULTIOP_NO_SOMETHINGX, y,
35, 28, _(limitIcons[i].desc),
limitIcons[i].icon, limitIcons[i].icon, limitIcons[i].icon);
y += 28 + 3;
skip = true;
}
}
}
// ////////////////////////////////////////////////////////////////////////////
// Colour functions
static bool safeToUseColour(unsigned player, unsigned otherPlayer)
{
// Player wants to take the colour from otherPlayer. May not take from a human otherPlayer, unless we're the host.
return player == otherPlayer || NetPlay.isHost || !isHumanPlayer(otherPlayer);
}
static void initChooser(int player)
{
// delete that players box,
widgDelete(psWScreen, MULTIOP_PLAYER_START + player);
// delete team chooser button
widgDelete(psWScreen, MULTIOP_TEAMS_START + player);
// delete 'ready' button
widgDelete(psWScreen, MULTIOP_READY_FORM_ID + player);
// delete 'colour' button
widgDelete(psWScreen, MULTIOP_COLOUR_START + player);
// remove any choosers already up
closeColourChooser();
closeTeamChooser();
}
static void addDifficultyChooser(int player)
{
closeColourChooser();
closeTeamChooser();
widgDelete(psWScreen, MULTIOP_AI_FORM);
widgDelete(psWScreen, MULTIOP_PLAYERS);
widgDelete(psWScreen, FRONTEND_SIDETEXT2);
difficultyChooserUp = player;
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
IntFormAnimated *aiForm = new IntFormAnimated(parent, false);
aiForm->id = MULTIOP_AI_FORM;
aiForm->setGeometry(MULTIOP_PLAYERSX, MULTIOP_PLAYERSY, MULTIOP_PLAYERSW, MULTIOP_PLAYERSH);
addSideText(FRONTEND_SIDETEXT2, MULTIOP_PLAYERSX - 3, MULTIOP_PLAYERSY, _("DIFFICULTY"));
for (int i = 0; i < 4; i++)
{
W_BUTINIT sButInit;
sButInit.formID = MULTIOP_AI_FORM;
sButInit.id = MULTIOP_DIFFICULTY_CHOOSE_START + i;
sButInit.x = 7;
sButInit.y = (MULTIOP_PLAYERHEIGHT + 5) * i + 4;
sButInit.width = MULTIOP_PLAYERWIDTH + 1;
sButInit.height = MULTIOP_PLAYERHEIGHT;
switch (i)
{
case 0: sButInit.pTip = _("Starts disadvantaged"); break;
case 1: sButInit.pTip = _("Plays nice"); break;
case 2: sButInit.pTip = _("No holds barred"); break;
2011-12-17 05:03:52 -08:00
case 3: sButInit.pTip = _("Starts with advantages"); break;
}
sButInit.pDisplay = displayDifficulty;
sButInit.UserData = i;
widgAddButton(psWScreen, &sButInit);
}
}
static void addAiChooser(int player)
{
closeColourChooser();
closeTeamChooser();
widgDelete(psWScreen, MULTIOP_AI_FORM);
widgDelete(psWScreen, MULTIOP_PLAYERS);
widgDelete(psWScreen, FRONTEND_SIDETEXT2);
aiChooserUp = player;
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
IntFormAnimated *aiForm = new IntFormAnimated(parent, false);
aiForm->id = MULTIOP_AI_FORM;
aiForm->setGeometry(MULTIOP_PLAYERSX, MULTIOP_PLAYERSY, MULTIOP_PLAYERSW, MULTIOP_PLAYERSH);
addSideText(FRONTEND_SIDETEXT2, MULTIOP_PLAYERSX - 3, MULTIOP_PLAYERSY, _("CHOOSE AI"));
W_BUTINIT sButInit;
sButInit.formID = MULTIOP_AI_FORM;
sButInit.x = 7;
sButInit.width = MULTIOP_PLAYERWIDTH + 1;
// Try to fit as many as possible, just got to make sure text fits in the 'box'.
// NOTE: Correct way would be to get the actual font size, render the text, and see what fits.
if (aidata.size() > 8)
{
sButInit.height = MULTIOP_PLAYERHEIGHT - 7;
}
else
{
sButInit.height = MULTIOP_PLAYERHEIGHT;
}
sButInit.pDisplay = displayAi;
// only need this button in (true) mp games
int mpbutton = NetPlay.bComms ? 1 : 0;
// cap AI's that are shown, since it looks a bit ugly. *FIXME*
int capAIs = aidata.size();
2013-10-28 19:17:13 -07:00
if (aidata.size() > 9)
{
2013-10-28 19:17:13 -07:00
debug(LOG_INFO, "You have too many AI's loaded for the GUI to handle. Only the first 10 will be shown.");
addConsoleMessage("You have too many AI's loaded for the GUI to handle. Only the first 10 will be shown.", DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
capAIs = 10;
}
// button height * how many AI + possible buttons (openclosed)
int gap = MULTIOP_PLAYERSH - ((sButInit.height)* (capAIs + 1 + mpbutton));
int gapDiv = capAIs - 1;
gap = std::min(gap, 5*gapDiv);
// Open button
if (mpbutton)
{
sButInit.id = MULTIOP_AI_OPEN;
sButInit.pTip = _("Allow human players to join in this slot");
sButInit.UserData = (UDWORD)AI_OPEN;
sButInit.y = 3; //Top most position
widgAddButton(psWScreen, &sButInit);
}
// Closed button
sButInit.pTip = _("Leave this slot unused");
sButInit.id = MULTIOP_AI_CLOSED;
sButInit.UserData = (UDWORD)AI_CLOSED;
if (mpbutton)
{
sButInit.y = sButInit.height;
}
else
{
sButInit.y = 0; //since we don't have the lone mpbutton, we can start at position 0
}
widgAddButton(psWScreen, &sButInit);
for (int i = 0; i < capAIs; i++)
{
sButInit.y = (sButInit.height*gapDiv + gap)*(i+1+mpbutton)/gapDiv; // +1 for 'closed', and possible +1 more for 'open' for MP games)
sButInit.pTip = aidata[i].tip;
sButInit.id = MULTIOP_AI_START + i;
sButInit.UserData = i;
widgAddButton(psWScreen, &sButInit);
}
}
static void closeAiChooser()
{
widgDelete(psWScreen, MULTIOP_AI_FORM);
widgDelete(psWScreen, FRONTEND_SIDETEXT2);
aiChooserUp = -1;
}
static void closeDifficultyChooser()
{
widgDelete(psWScreen, MULTIOP_AI_FORM);
widgDelete(psWScreen, FRONTEND_SIDETEXT2);
difficultyChooserUp = -1;
}
static void closePositionChooser()
{
positionChooserUp = -1;
}
2011-01-03 05:48:41 -08:00
static int playerBoxHeight(int player)
{
int gap = MULTIOP_PLAYERSH - MULTIOP_TEAMSHEIGHT * game.maxPlayers;
2011-01-03 05:48:41 -08:00
int gapDiv = game.maxPlayers - 1;
gap = std::min(gap, 5*gapDiv);
STATIC_ASSERT(MULTIOP_TEAMSHEIGHT == MULTIOP_PLAYERHEIGHT); // Why are these different defines?
return (MULTIOP_TEAMSHEIGHT*gapDiv + gap)*NetPlay.players[player].position/gapDiv;
2011-01-03 05:48:41 -08:00
}
static void addPositionChooser(int player)
{
closeColourChooser();
closeTeamChooser();
closePositionChooser();
closeAiChooser();
closeDifficultyChooser();
positionChooserUp = player;
addPlayerBox(true);
}
static void addColourChooser(UDWORD player)
{
UDWORD i;
initChooser(player);
// add form.
addBlueForm(MULTIOP_PLAYERS,MULTIOP_COLCHOOSER_FORM,"",
8,
2011-01-03 05:48:41 -08:00
playerBoxHeight(player),
MULTIOP_ROW_WIDTH,MULTIOP_PLAYERHEIGHT);
// add the flags
int flagW = iV_GetImageWidth(FrontImages, IMAGE_PLAYERN);
int flagH = iV_GetImageHeight(FrontImages, IMAGE_PLAYERN);
int space = MULTIOP_ROW_WIDTH - 0 - flagW*MAX_PLAYERS_IN_GUI;
2011-01-03 05:48:41 -08:00
int spaceDiv = MAX_PLAYERS_IN_GUI;
space = std::min(space, 5*spaceDiv);
for (i = 0; i < MAX_PLAYERS_IN_GUI; i++)
{
addMultiBut(psWScreen, MULTIOP_COLCHOOSER_FORM, MULTIOP_COLCHOOSER + getPlayerColour(i),
2011-01-03 05:48:41 -08:00
i*(flagW*spaceDiv + space)/spaceDiv + 7, 4, // x, y
flagW, flagH, // w, h
getPlayerColourName(i), IMAGE_PLAYERN, IMAGE_PLAYERN_HI, IMAGE_PLAYERN_HI, getPlayerColour(i));
if( !safeToUseColour(selectedPlayer,i))
{
widgSetButtonState(psWScreen, MULTIOP_COLCHOOSER + getPlayerColour(i), WBUT_DISABLE);
}
}
colourChooserUp = player;
}
static void closeColourChooser(void)
{
colourChooserUp = -1;
widgDelete(psWScreen,MULTIOP_COLCHOOSER_FORM);
}
static void changeTeam(UBYTE player, UBYTE team)
{
NetPlay.players[player].team = team;
debug(LOG_WZ, "set %d as new team for player %d", team, player);
NETBroadcastPlayerInfo(player);
netPlayersUpdated = true;
}
static bool SendTeamRequest(UBYTE player, UBYTE chosenTeam)
{
if(NetPlay.isHost) // do or request the change.
{
changeTeam(player, chosenTeam); // do the change, remember only the host can do this to avoid confusion.
}
else
{
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_TEAMREQUEST);
NETuint8_t(&player);
NETuint8_t(&chosenTeam);
NETend();
}
return true;
}
bool recvTeamRequest(NETQUEUE queue)
{
UBYTE player, team;
if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet.
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
ASSERT(false, "Host only routine detected for client!");
return true;
}
NETbeginDecode(queue, NET_TEAMREQUEST);
NETuint8_t(&player);
NETuint8_t(&team);
NETend();
if (player >= MAX_PLAYERS || team >= MAX_PLAYERS)
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
debug(LOG_NET, "NET_TEAMREQUEST invalid, player %d team, %d", (int) player, (int) team);
debug(LOG_ERROR, "Invalid NET_TEAMREQUEST from player %d: Tried to change player %d (team %d)",
queue.index, (int)player, (int)team);
return false;
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(player) != queue.index)
{
HandleBadParam("NET_TEAMREQUEST given incorrect params.", player, queue.index);
return false;
}
if (NetPlay.players[player].team != team)
{
resetReadyStatus(false);
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
debug(LOG_NET, "%s is now part of team: %d", NetPlay.players[player].name, (int) team);
changeTeam(player, team); // we do this regardless, in case of sync issues
return true;
}
static bool SendReadyRequest(UBYTE player, bool bReady)
{
if(NetPlay.isHost) // do or request the change.
{
return changeReadyStatus(player, bReady);
}
else
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_READY_REQUEST);
NETuint8_t(&player);
NETbool(&bReady);
NETend();
}
return true;
}
bool recvReadyRequest(NETQUEUE queue)
{
UBYTE player;
bool bReady;
if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet.
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
ASSERT(false, "Host only routine detected for client!");
return true;
}
NETbeginDecode(queue, NET_READY_REQUEST);
NETuint8_t(&player);
NETbool(&bReady);
NETend();
if (player >= MAX_PLAYERS)
{
debug(LOG_ERROR, "Invalid NET_READY_REQUEST from player %d: player id = %d",
queue.index, (int)player);
return false;
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(player) != queue.index)
{
HandleBadParam("NET_READY_REQUEST given incorrect params.", player, queue.index);
return false;
}
// do not allow players to select 'ready' if we are sending a map too them!
// TODO: make a new icon to show this state?
if (NetPlay.players[player].wzFile.isSending)
{
return false;
}
return changeReadyStatus((UBYTE)player, bReady);
}
static bool changeReadyStatus(UBYTE player, bool bReady)
{
drawReadyButton(player);
NetPlay.players[player].ready = bReady;
NETBroadcastPlayerInfo(player);
netPlayersUpdated = true;
return true;
}
static bool changePosition(UBYTE player, UBYTE position)
{
int i;
for (i = 0; i < MAX_PLAYERS; i++)
{
if (NetPlay.players[i].position == position)
{
debug(LOG_NET, "Swapping positions between players %d(%d) and %d(%d)",
player, NetPlay.players[player].position, i, NetPlay.players[i].position);
NetPlay.players[i].position = NetPlay.players[player].position;
NetPlay.players[player].position = position;
NETBroadcastTwoPlayerInfo(player, i);
netPlayersUpdated = true;
return true;
}
}
debug(LOG_ERROR, "Failed to swap positions for player %d, position %d", (int)player, (int)position);
if (player < game.maxPlayers && position < game.maxPlayers)
{
2012-10-01 10:54:27 -07:00
debug(LOG_NET, "corrupted positions: player (%u) new position (%u) old position (%d)", player, position, NetPlay.players[player].position);
// Positions were corrupted. Attempt to fix.
NetPlay.players[player].position = position;
NETBroadcastPlayerInfo(player);
netPlayersUpdated = true;
return true;
}
return false;
}
bool changeColour(unsigned player, int col, bool isHost)
{
if (col < 0 || col >= MAX_PLAYERS_IN_GUI)
{
return true;
}
if (getPlayerColour(player) == col)
{
return true; // Nothing to do.
}
for (unsigned i = 0; i < MAX_PLAYERS; ++i)
{
if (getPlayerColour(i) == col)
{
if (!isHost && NetPlay.players[i].allocated)
{
return true; // May not swap.
}
debug(LOG_NET, "Swapping colours between players %d(%d) and %d(%d)",
player, getPlayerColour(player), i, getPlayerColour(i));
setPlayerColour(i, getPlayerColour(player));
NetPlay.players[i].colour = getPlayerColour(player);
setPlayerColour(player, col);
NetPlay.players[player].colour = col;
NETBroadcastTwoPlayerInfo(player, i);
netPlayersUpdated = true;
return true;
}
}
debug(LOG_ERROR, "Failed to swap colours for player %d, colour %d", (int)player, (int)col);
if (player < game.maxPlayers && col < MAX_PLAYERS)
{
// Colours were corrupted. Attempt to fix.
2012-10-01 10:54:27 -07:00
debug(LOG_NET, "corrupted colours: player (%u) new colour (%u) old colour (%d)", player, col, NetPlay.players[player].colour);
setPlayerColour(player, col);
NetPlay.players[player].colour = col;
NETBroadcastPlayerInfo(player);
netPlayersUpdated = true;
return true;
}
return false;
}
static bool SendColourRequest(UBYTE player, UBYTE col)
{
if(NetPlay.isHost) // do or request the change
{
return changeColour(player, col, true);
}
else
{
// clients tell the host which color they want
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_COLOURREQUEST);
NETuint8_t(&player);
NETuint8_t(&col);
NETend();
}
return true;
}
static bool SendPositionRequest(UBYTE player, UBYTE position)
{
if(NetPlay.isHost) // do or request the change
{
return changePosition(player, position);
}
else
{
debug(LOG_NET, "Requesting the host to change our position. From %d to %d", player, position);
// clients tell the host which position they want
NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_POSITIONREQUEST);
NETuint8_t(&player);
NETuint8_t(&position);
NETend();
}
return true;
}
bool recvColourRequest(NETQUEUE queue)
{
UBYTE player, col;
if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet.
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
ASSERT(false, "Host only routine detected for client!");
return true;
}
NETbeginDecode(queue, NET_COLOURREQUEST);
NETuint8_t(&player);
NETuint8_t(&col);
NETend();
if (player >= MAX_PLAYERS)
{
debug(LOG_ERROR, "Invalid NET_COLOURREQUEST from player %d: Tried to change player %d to colour %d",
queue.index, (int)player, (int)col);
return false;
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(player) != queue.index)
{
HandleBadParam("NET_COLOURREQUEST given incorrect params.", player, queue.index);
return false;
}
resetReadyStatus(false);
return changeColour(player, col, false);
}
bool recvPositionRequest(NETQUEUE queue)
{
UBYTE player, position;
if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet.
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
ASSERT(false, "Host only routine detected for client!");
return true;
}
NETbeginDecode(queue, NET_POSITIONREQUEST);
NETuint8_t(&player);
NETuint8_t(&position);
NETend();
debug(LOG_NET, "Host received position request from player %d to %d", player, position);
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (player >= MAX_PLAYERS || position >= MAX_PLAYERS)
{
debug(LOG_ERROR, "Invalid NET_POSITIONREQUEST from player %d: Tried to change player %d to %d",
queue.index, (int)player, (int)position);
return false;
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(player) != queue.index)
{
HandleBadParam("NET_POSITIONREQUEST given incorrect params.", player, queue.index);
return false;
}
resetReadyStatus(false);
return changePosition(player, position);
}
// If so, return that team; if not, return -1; if there are no players, return team MAX_PLAYERS.
int allPlayersOnSameTeam(int except)
{
int minTeam = MAX_PLAYERS, maxTeam = 0;
for (int i = 0; i < game.maxPlayers; ++i)
{
if (i != except && (NetPlay.players[i].allocated || NetPlay.players[i].ai >= 0))
{
minTeam = std::min(minTeam, NetPlay.players[i].team);
maxTeam = std::max(maxTeam, NetPlay.players[i].team);
}
}
if (minTeam == MAX_PLAYERS || minTeam == maxTeam)
{
return minTeam; // Players all on same team.
}
return -1; // Players not all on same team.
}
/*
* Opens a menu for a player to choose a team
* 'player' is a player id of the player who will get a new team assigned
*/
static void addTeamChooser(UDWORD player)
{
UDWORD i;
int disallow = allPlayersOnSameTeam(player);
debug(LOG_NET, "Opened team chooser for %d, current team: %d", player, NetPlay.players[player].team);
initChooser(player);
// add form.
addBlueForm(MULTIOP_PLAYERS, MULTIOP_TEAMCHOOSER_FORM, "", 8, playerBoxHeight(player), MULTIOP_ROW_WIDTH, MULTIOP_TEAMSHEIGHT);
2011-01-03 05:48:41 -08:00
int teamW = iV_GetImageWidth(FrontImages, IMAGE_TEAM0);
int teamH = iV_GetImageHeight(FrontImages, IMAGE_TEAM0);
int space = MULTIOP_ROW_WIDTH - 4 - teamW*(game.maxPlayers + 1);
2011-01-03 05:48:41 -08:00
int spaceDiv = game.maxPlayers;
space = std::min(space, 3*spaceDiv);
// add the teams, skipping the one we CAN'T be on (if applicable)
for (i = 0; i < game.maxPlayers; i++)
{
if (i != disallow)
{
2011-01-03 05:48:41 -08:00
addMultiBut(psWScreen, MULTIOP_TEAMCHOOSER_FORM, MULTIOP_TEAMCHOOSER + i, i * (teamW*spaceDiv + space)/spaceDiv + 3, 6, teamW, teamH, _("Team"), IMAGE_TEAM0 + i , IMAGE_TEAM0_HI + i, IMAGE_TEAM0_HI + i);
}
// may want to add some kind of 'can't do' icon instead of being blank?
}
// add a kick button
if (player != selectedPlayer && NetPlay.bComms && NetPlay.isHost && NetPlay.players[player].allocated)
{
const int imgwidth = iV_GetImageWidth(FrontImages, IMAGE_NOJOIN);
const int imgheight = iV_GetImageHeight(FrontImages, IMAGE_NOJOIN);
addMultiBut(psWScreen, MULTIOP_TEAMCHOOSER_FORM, MULTIOP_TEAMCHOOSER_KICK, MULTIOP_ROW_WIDTH - imgwidth - 4, 8, imgwidth, imgheight,
("Kick player"), IMAGE_NOJOIN, IMAGE_NOJOIN, IMAGE_NOJOIN);
}
teamChooserUp = player;
}
/*
* Closes Team Chooser dialog box, if there was any open
*/
static void closeTeamChooser(void)
{
teamChooserUp = -1;
widgDelete(psWScreen,MULTIOP_TEAMCHOOSER_FORM); //only once!
}
static void drawReadyButton(UDWORD player)
{
int disallow = allPlayersOnSameTeam(-1);
// delete 'ready' botton form
widgDelete(psWScreen, MULTIOP_READY_FORM_ID + player);
// add form to hold 'ready' botton
addBlueForm(MULTIOP_PLAYERS,MULTIOP_READY_FORM_ID + player,"",
7 + MULTIOP_PLAYERWIDTH - MULTIOP_READY_WIDTH,
2011-01-03 05:48:41 -08:00
playerBoxHeight(player),
MULTIOP_READY_WIDTH,MULTIOP_READY_HEIGHT);
WIDGET *parent = widgGetFromID(psWScreen, MULTIOP_READY_FORM_ID + player);
if (!NetPlay.players[player].allocated && NetPlay.players[player].ai >= 0)
{
int icon = difficultyIcon(NetPlay.players[player].difficulty);
addMultiBut(psWScreen, MULTIOP_READY_FORM_ID + player, MULTIOP_DIFFICULTY_INIT_START + player, 6, 4, MULTIOP_READY_WIDTH, MULTIOP_READY_HEIGHT,
locked.difficulty ? _("Difficulty locked") : (NetPlay.isHost ? _("Click to change difficulty") : NULL), icon, icon, icon);
return;
}
else if (!NetPlay.players[player].allocated)
{
return; // closed or open
}
if (disallow != -1)
{
return;
}
bool isMe = player == selectedPlayer;
bool isReady = NetPlay.players[player].ready;
char const *const toolTips[2][2] = {{_("Waiting for player"), _("Player is ready")}, {_("Click when ready"), _("Waiting for other players")}};
unsigned images[2][2] = {{IMAGE_CHECK_OFF, IMAGE_CHECK_ON}, {IMAGE_CHECK_OFF_HI, IMAGE_CHECK_ON_HI}};
// draw 'ready' button
addMultiBut(psWScreen, MULTIOP_READY_FORM_ID + player, MULTIOP_READY_START + player, 3, 10, MULTIOP_READY_WIDTH, MULTIOP_READY_HEIGHT,
toolTips[isMe][isReady], images[0][isReady], images[0][isReady], images[isMe][isReady]);
W_LABEL *label = new W_LABEL(parent);
label->id = MULTIOP_READY_START + MAX_PLAYERS + player;
label->setGeometry(0, 0, MULTIOP_READY_WIDTH, 17);
label->setTextAlignment(WLAB_ALIGNBOTTOM);
label->setFont(font_small, WZCOL_TEXT_BRIGHT);
label->setString(_("READY?"));
}
static bool canChooseTeamFor(int i)
{
return (i == selectedPlayer || NetPlay.isHost);
}
// ////////////////////////////////////////////////////////////////////////////
// box for players.
void addPlayerBox(bool players)
{
// if background isn't there, then return since were not ready to draw the box yet!
if(widgGetFromID(psWScreen,FRONTEND_BACKDROP) == NULL)
{
return;
}
widgDelete(psWScreen,MULTIOP_PLAYERS); // del player window
widgDelete(psWScreen,FRONTEND_SIDETEXT2); // del text too,
if (aiChooserUp >= 0)
{
addAiChooser(aiChooserUp);
return;
}
else if (difficultyChooserUp >= 0)
{
addDifficultyChooser(difficultyChooserUp);
return;
}
// draw player window
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
IntFormAnimated *playersForm = new IntFormAnimated(parent, false);
playersForm->id = MULTIOP_PLAYERS;
playersForm->setGeometry(MULTIOP_PLAYERSX, MULTIOP_PLAYERSY, MULTIOP_PLAYERSW, MULTIOP_PLAYERSH);
addSideText(FRONTEND_SIDETEXT2, MULTIOP_PLAYERSX-3, MULTIOP_PLAYERSY,_("PLAYERS"));
if(players)
{
int team = -1;
bool allOnSameTeam = true;
for (int i = 0; i < game.maxPlayers; i++)
{
if (game.skDiff[i] || isHumanPlayer(i))
{
int myTeam = alliancesSetTeamsBeforeGame(game.alliance)? NetPlay.players[i].team : i;
if (team == -1)
{
team = myTeam;
}
else if (myTeam != team)
{
allOnSameTeam = false;
break; // We just need to know if we have enough to start a game
}
}
}
for (int i = 0; i < game.maxPlayers; i++)
{
if (positionChooserUp >= 0 && positionChooserUp != i && (NetPlay.isHost || !isHumanPlayer(i)))
{
W_BUTINIT sButInit;
sButInit.formID = MULTIOP_PLAYERS;
sButInit.id = MULTIOP_PLAYER_START + i;
sButInit.x = 7;
2011-01-03 05:48:41 -08:00
sButInit.y = playerBoxHeight(i);
sButInit.width = MULTIOP_PLAYERWIDTH + 1;
sButInit.height = MULTIOP_PLAYERHEIGHT;
sButInit.pTip = _("Click to change to this slot");
sButInit.pDisplay = displayPosition;
sButInit.UserData = i;
widgAddButton(psWScreen, &sButInit);
continue;
}
else if (i == colourChooserUp)
{
addColourChooser(i);
continue;
}
else if (i == teamChooserUp)
{
addTeamChooser(i);
continue;
}
else if (ingame.localOptionsReceived)
{
//add team chooser
W_BUTINIT sButInit;
sButInit.formID = MULTIOP_PLAYERS;
sButInit.id = MULTIOP_TEAMS_START+i;
sButInit.x = 7;
2011-01-03 05:48:41 -08:00
sButInit.y = playerBoxHeight(i);
sButInit.width = MULTIOP_TEAMSWIDTH;
sButInit.height = MULTIOP_TEAMSHEIGHT;
if (canChooseTeamFor(i) && !locked.teams)
{
sButInit.pTip = _("Choose Team");
}
else if (locked.teams)
{
sButInit.pTip = _("Teams locked");
}
sButInit.pDisplay = displayTeamChooser;
sButInit.UserData = i;
if (teamChooserUp == i && colourChooserUp < 0)
{
addTeamChooser(i);
}
else if (alliancesSetTeamsBeforeGame(game.alliance))
{
// only if not disabled and in locked teams mode
widgAddButton(psWScreen, &sButInit);
}
}
// draw player colour
W_BUTINIT sColInit;
sColInit.formID = MULTIOP_PLAYERS;
sColInit.id = MULTIOP_COLOUR_START + i;
sColInit.x = 7 + MULTIOP_TEAMSWIDTH;
2011-01-03 05:48:41 -08:00
sColInit.y = playerBoxHeight(i);
sColInit.width = MULTIOP_COLOUR_WIDTH;
sColInit.height = MULTIOP_PLAYERHEIGHT;
if (selectedPlayer == i || NetPlay.isHost)
{
sColInit.pTip = _("Click to change player colour");
}
sColInit.pDisplay = displayColour;
sColInit.UserData = i;
widgAddButton(psWScreen, &sColInit);
if (ingame.localOptionsReceived)
{
if (!allOnSameTeam)
{
drawReadyButton(i);
}
// draw player info box
W_BUTINIT sButInit;
sButInit.formID = MULTIOP_PLAYERS;
sButInit.id = MULTIOP_PLAYER_START+i;
sButInit.x = 7 + MULTIOP_TEAMSWIDTH + MULTIOP_COLOUR_WIDTH;
2011-01-03 05:48:41 -08:00
sButInit.y = playerBoxHeight(i);
sButInit.width = MULTIOP_PLAYERWIDTH - MULTIOP_TEAMSWIDTH - MULTIOP_READY_WIDTH - MULTIOP_COLOUR_WIDTH;
sButInit.height = MULTIOP_PLAYERHEIGHT;
if ((selectedPlayer == i || NetPlay.isHost) && NetPlay.players[i].allocated && !locked.position)
{
sButInit.pTip = _("Click to change player position");
}
else if (!NetPlay.players[i].allocated && NetPlay.isHost && !locked.ai)
{
sButInit.pTip = _("Click to change AI");
}
if (NetPlay.players[i].allocated && !getMultiStats(i).identity.empty())
{
if (!sButInit.pTip.isEmpty())
{
sButInit.pTip += "\n";
}
EcKey::Key bytes = getMultiStats(i).identity.toBytes(EcKey::Public);
sButInit.pTip += _("Player ID: ");
if (!bytes.empty())
{
sButInit.pTip += sha256Sum(&bytes[0], bytes.size()).toString().substr(0, 20).c_str();
}
else
{
sButInit.pTip += _("(none)");
}
}
sButInit.pDisplay = displayPlayer;
sButInit.UserData = i;
widgAddButton(psWScreen, &sButInit);
}
}
}
}
/*
* Notify all players of host launching the game
*/
static void SendFireUp(void)
{
uint32_t randomSeed = rand(); // Pick a random random seed for the synchronised random number generator.
NETbeginEncode(NETbroadcastQueue(), NET_FIREUP);
NETuint32_t(&randomSeed);
NETend();
printSearchPath();
gameSRand(randomSeed); // Set the seed for the synchronised random number generator. The clients will use the same seed.
}
// host kicks a player from a game.
void kickPlayer(uint32_t player_id, const char *reason, LOBBY_ERROR_TYPES type)
{
// send a kick msg
NETbeginEncode(NETbroadcastQueue(), NET_KICK);
NETuint32_t(&player_id);
NETstring( (char *) reason, MAX_KICK_REASON);
NETenum(&type);
NETend();
NETflush();
wzDelay(300);
debug(LOG_NET, "Kicking player %u (%s).",
(unsigned int)player_id, getPlayerName(player_id));
NETplayerKicked(player_id);
}
static void addChatBox(void)
{
if(widgGetFromID(psWScreen,FRONTEND_TOPFORM))
{
widgDelete(psWScreen,FRONTEND_TOPFORM);
}
if(widgGetFromID(psWScreen,MULTIOP_CHATBOX))
{
return;
}
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
IntFormAnimated *chatBox = new IntFormAnimated(parent);
chatBox->id = MULTIOP_CHATBOX;
chatBox->setGeometry(MULTIOP_CHATBOXX, MULTIOP_CHATBOXY, MULTIOP_CHATBOXW, MULTIOP_CHATBOXH);
addSideText(FRONTEND_SIDETEXT4,MULTIOP_CHATBOXX-3,MULTIOP_CHATBOXY,_("CHAT"));
flushConsoleMessages(); // add the chatbox.
initConsoleMessages();
enableConsoleDisplay(true);
setConsoleBackdropStatus(false);
setConsoleSizePos(MULTIOP_CHATBOXX+4+D_W, MULTIOP_CHATBOXY+14+D_H, MULTIOP_CHATBOXW-4);
setConsolePermanence(true,true);
setConsoleLineInfo(5); // use x lines on chat window
W_EDBINIT sEdInit; // add the edit box
sEdInit.formID = MULTIOP_CHATBOX;
sEdInit.id = MULTIOP_CHATEDIT;
sEdInit.x = MULTIOP_CHATEDITX;
sEdInit.y = MULTIOP_CHATEDITY;
sEdInit.width = MULTIOP_CHATEDITW;
sEdInit.height = MULTIOP_CHATEDITH;
sEdInit.pUserData = NULL;
sEdInit.pBoxDisplay = displayChatEdit;
widgAddEditBox(psWScreen, &sEdInit);
if (*getModList())
{
std::string modListMessage = _("Mod: ");
modListMessage += getModList();
addConsoleMessage(modListMessage.c_str(), DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
if (NetPlay.bComms)
{
addConsoleMessage(_("All players need to have the same mods to join your game."),DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
}
}
return;
}
static void addConsoleBox(void)
{
if(widgGetFromID(psWScreen, FRONTEND_TOPFORM))
{
widgDelete(psWScreen, FRONTEND_TOPFORM);
}
if(widgGetFromID(psWScreen, MULTIOP_CONSOLEBOX))
{
return;
}
WIDGET *parent = widgGetFromID(psWScreen, FRONTEND_BACKDROP);
IntFormAnimated *consoleBox = new IntFormAnimated(parent);
consoleBox->id = MULTIOP_CONSOLEBOX;
consoleBox->setGeometry(MULTIOP_CONSOLEBOXX, MULTIOP_CONSOLEBOXY, MULTIOP_CONSOLEBOXW, MULTIOP_CONSOLEBOXH);
flushConsoleMessages(); // add the chatbox.
initConsoleMessages();
enableConsoleDisplay(true);
setConsoleBackdropStatus(false);
setConsoleSizePos(MULTIOP_CONSOLEBOXX + 4 + D_W, MULTIOP_CONSOLEBOXY + 14 + D_H, MULTIOP_CONSOLEBOXW - 4);
setConsolePermanence(true, true);
setConsoleLineInfo(3); // use x lines on chat window
addConsoleMessage(_("Connecting to the lobby server..."), DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
displayConsoleMessages();
return;
}
// ////////////////////////////////////////////////////////////////////////////
static void disableMultiButs(void)
{
// edit box icons.
widgSetButtonState(psWScreen, MULTIOP_GNAME_ICON, WBUT_DISABLE);
widgSetButtonState(psWScreen, MULTIOP_MAP_ICON, WBUT_DISABLE);
// edit boxes
widgSetButtonState(psWScreen,MULTIOP_GNAME,WEDBS_DISABLE);
if (!NetPlay.isHost)
{
((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_GAMETYPE))->disable(); // Scavengers.
((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_BASETYPE))->disable(); // camapign subtype.
((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_POWER))->disable(); // pow levels
((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_ALLIANCES))->disable();
}
}
////////////////////////////////////////////////////////////////////////////
static void stopJoining(void)
{
dwSelectedGame = 0;
reloadMPConfig(); // reload own settings
debug(LOG_NET,"player %u (Host is %s) stopping.", selectedPlayer, NetPlay.isHost ? "true" : "false");
if(bHosted) // cancel a hosted game.
{
// annouce we are leaving...
debug(LOG_NET, "Host is quitting game...");
NETbeginEncode(NETbroadcastQueue(), NET_HOST_DROPPED);
NETend();
sendLeavingMsg(); // say goodbye
NETclose(); // quit running game.
bHosted = false; // stop host mode.
widgDelete(psWScreen,FRONTEND_BACKDROP); // refresh options screen.
startMultiOptions(false);
ingame.localJoiningInProgress = false;
return;
}
else if(ingame.localJoiningInProgress) // cancel a joined game.
{
debug(LOG_NET, "Canceling game...");
sendLeavingMsg(); // say goodbye
NETclose(); // quit running game.
// if we were in a midle of transfering a file, then close the file handle
if (NetPlay.pMapFileHandle)
{
debug(LOG_NET, "closing aborted file"); // no need to delete it, we do size check on (map) file
PHYSFS_close(NetPlay.pMapFileHandle);
NetPlay.pMapFileHandle = NULL;
}
ingame.localJoiningInProgress = false; // reset local flags
ingame.localOptionsReceived = false;
if(!ingame.bHostSetup && NetPlay.isHost) // joining and host was transfered.
{
NetPlay.isHost = false;
}
changeTitleMode(MULTI);
selectedPlayer = 0;
realSelectedPlayer = 0;
return;
}
debug(LOG_NET, "We have stopped joining.");
changeTitleMode(lastTitleMode);
selectedPlayer = 0;
realSelectedPlayer = 0;
if (ingame.bHostSetup)
{
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
}
}
static void loadMapSettings1()
{
char aFileName[256];
const char *ininame = challengeActive ? sRequestResult : aFileName;
LEVEL_DATASET *psLevel = levFindDataSet(game.map, &game.hash);
ASSERT_OR_RETURN(, psLevel, "No level found for %s", game.map);
sstrcpy(aFileName, psLevel->apDataFiles[psLevel->game]);
aFileName[strlen(aFileName) - 4] = '\0';
sstrcat(aFileName, ".ini");
WzConfig ini(ininame, WzConfig::ReadOnly);
if (!PHYSFS_exists(ininame))
{
return;
}
ini.beginGroup("locked"); // GUI lockdown
locked.power = ini.value("power", challengeActive).toBool();
locked.alliances = ini.value("alliances", challengeActive).toBool();
locked.teams = ini.value("teams", challengeActive).toBool();
locked.difficulty = ini.value("difficulty", challengeActive).toBool();
locked.ai = ini.value("ai", challengeActive).toBool();
locked.scavengers = ini.value("scavengers", challengeActive).toBool();
locked.position = ini.value("position", challengeActive).toBool();
locked.bases = ini.value("bases", challengeActive).toBool();
ini.endGroup();
ini.beginGroup("defaults");
game.scavengers = ini.value("scavengers", game.scavengers).toBool();
game.base = ini.value("bases", game.base).toInt();
game.alliance = ini.value("alliances", game.alliance).toInt();
if (ini.contains("powerLevel"))
{
game.power = ini.value("powerLevel", game.power).toInt();
}
ini.endGroup();
}
static void loadMapSettings2()
{
char aFileName[256];
const char *ininame = challengeActive ? sRequestResult : aFileName;
LEVEL_DATASET *psLevel = levFindDataSet(game.map, &game.hash);
ASSERT_OR_RETURN(, psLevel, "No level found for %s", game.map);
sstrcpy(aFileName, psLevel->apDataFiles[psLevel->game]);
aFileName[strlen(aFileName) - 4] = '\0';
sstrcat(aFileName, ".ini");
WzConfig ini(ininame, WzConfig::ReadOnly);
if (!PHYSFS_exists(ininame))
{
return;
}
int offbyone = 0; // for compatibility with 3.1 and earlier challenges
ini.beginGroup("challenge"); // backwards compatibility mode
if (challengeActive && ini.value("version", 1).toInt() <= 1)
{
offbyone = 1; // old version, counts from 1
}
ini.endGroup();
for (int i = 0; i < MAX_PLAYERS; i++)
{
ini.beginGroup("player_" + QString::number(i + offbyone));
if (ini.contains("team"))
{
NetPlay.players[i].team = ini.value("team").toInt() - offbyone;
}
else if (challengeActive) // team is a required key for challenges
{
NetPlay.players[i].ai = AI_CLOSED;
}
if (ini.contains("name"))
{
sstrcpy(nameOverrides[i], ini.value("name").toString().toUtf8().constData());
}
if (ini.contains("position"))
{
changePosition(i, ini.value("position", NetPlay.players[i].position).toInt());
}
if (ini.contains("difficulty"))
{
QString value = ini.value("difficulty", "Medium").toString();
for (int j = 0; j < ARRAY_SIZE(difficultyList); j++)
{
if (strcasecmp(difficultyList[j], value.toUtf8().constData()) == 0)
{
game.skDiff[i] = difficultyValue[j];
NetPlay.players[i].difficulty = j;
}
}
}
ini.endGroup();
}
}
/*
* Process click events on the multiplayer/skirmish options screen
* 'id' is id of the button that was pressed
*/
static void processMultiopWidgets(UDWORD id)
{
PLAYERSTATS playerStats;
// host, who is setting up the game
if((ingame.bHostSetup && !bHosted))
{
switch(id) // Options buttons
{
case MULTIOP_GNAME: // we get this when nec.
sstrcpy(game.name,widgGetString(psWScreen, MULTIOP_GNAME));
break;
case MULTIOP_GNAME_ICON:
break;
case MULTIOP_MAP:
widgDelete(psWScreen, MULTIOP_PLAYERS);
widgDelete(psWScreen, FRONTEND_SIDETEXT2); // del text too,
debug(LOG_WZ, "processMultiopWidgets[MULTIOP_MAP_ICON]: %s.wrf", MultiCustomMapsPath);
addMultiRequest(MultiCustomMapsPath, ".wrf", MULTIOP_MAP, current_tech, 0, widgGetString(psWScreen, MULTIOP_MAP));
widgSetString(psWScreen, MULTIOP_MAP+1 ,game.map); //What a horrible hack! FIX ME! (See addBlueForm())
widgReveal(psWScreen, MULTIOP_MAP_MOD);
break;
case MULTIOP_MAP_ICON:
widgDelete(psWScreen,MULTIOP_PLAYERS);
widgDelete(psWScreen,FRONTEND_SIDETEXT2); // del text too,
debug(LOG_WZ, "processMultiopWidgets[MULTIOP_MAP_ICON]: %s.wrf", MultiCustomMapsPath);
addMultiRequest(MultiCustomMapsPath, ".wrf", MULTIOP_MAP, current_tech, current_numplayers);
break;
case MULTIOP_MAP_PREVIEW:
loadMapPreview(true);
break;
}
}
// host who is setting up or has hosted
if(ingame.bHostSetup)// || NetPlay.isHost) // FIXME Was: if(ingame.bHostSetup);{} ??? Note the ; !
{
switch(id)
{
case MULTIOP_GAMETYPE:
game.scavengers = ((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_GAMETYPE))->currentValue();
resetReadyStatus(false);
if(bHosted)
{
sendOptions();
}
break;
case MULTIOP_BASETYPE:
game.base = ((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_BASETYPE))->currentValue();
addGameOptions();
resetReadyStatus(false);
if(bHosted)
{
sendOptions();
disableMultiButs();
}
break;
case MULTIOP_ALLIANCES:
game.alliance = ((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_ALLIANCES))->currentValue();
resetReadyStatus(false);
netPlayersUpdated = true;
if(bHosted)
{
sendOptions();
}
break;
case MULTIOP_POWER: // set power level
game.power = ((MultichoiceWidget *)widgGetFromID(psWScreen, MULTIOP_POWER))->currentValue();
resetReadyStatus(false);
if(bHosted)
{
sendOptions();
}
break;
case MULTIOP_PASSWORD_EDIT:
{
unsigned result = widgGetButtonState(psWScreen, MULTIOP_PASSWORD_BUT);
if (result != 0)
{
break;
}
}
// Continue, do not break, since we just set a password.
case MULTIOP_PASSWORD_BUT:
{
char buf[255];
bool willSet = widgGetButtonState(psWScreen, MULTIOP_PASSWORD_BUT) == 0;
debug(LOG_NET, "Password button hit, %d", (int)willSet);
widgSetButtonState(psWScreen, MULTIOP_PASSWORD_BUT, willSet? WBUT_CLICKLOCK : 0);
widgSetButtonState(psWScreen, MULTIOP_PASSWORD_EDIT, willSet? WEDBS_DISABLE : 0);
if (willSet)
{
char game_password[64];
sstrcpy(game_password, widgGetString(psWScreen, MULTIOP_PASSWORD_EDIT));
NETsetGamePassword(game_password);
// say password is now required to join games?
ssprintf(buf, _("*** password [%s] is now required! ***"), NetPlay.gamePassword);
addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
}
else
{
NETresetGamePassword();
ssprintf(buf, _("*** password is NOT required! ***"));
addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
}
NETGameLocked(willSet);
}
break;
}
}
// these work all the time.
switch(id)
{
case MULTIOP_MAP_MOD:
char buf[256];
ssprintf(buf, _("This is a map-mod, it can change your playing experience!"));
addConsoleMessage(buf, DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
break;
case MULTIOP_STRUCTLIMITS:
changeTitleMode(MULTILIMIT);
break;
case MULTIOP_PNAME:
sstrcpy(sPlayer,widgGetString(psWScreen, MULTIOP_PNAME));
// chop to 15 chars..
while(strlen(sPlayer) > 15) // clip name.
{
sPlayer[strlen(sPlayer)-1]='\0';
}
// update string.
widgSetString(psWScreen, MULTIOP_PNAME,sPlayer);
removeWildcards((char*)sPlayer);
printConsoleNameChange(NetPlay.players[selectedPlayer].name, sPlayer);
NETchangePlayerName(selectedPlayer, (char*)sPlayer); // update if joined.
loadMultiStats((char*)sPlayer,&playerStats);
setMultiStats(selectedPlayer,playerStats,false);
setMultiStats(selectedPlayer,playerStats,true);
netPlayersUpdated = true;
break;
case MULTIOP_PNAME_ICON:
widgDelete(psWScreen,MULTIOP_PLAYERS);
widgDelete(psWScreen,FRONTEND_SIDETEXT2); // del text too,
addMultiRequest(MultiPlayersPath, ".sta", MULTIOP_PNAME, 0, 0);
break;
case MULTIOP_HOST:
debug(LOG_NET, "MULTIOP_HOST enabled");
sstrcpy(game.name, widgGetString(psWScreen, MULTIOP_GNAME)); // game name
sstrcpy(sPlayer, widgGetString(psWScreen, MULTIOP_PNAME)); // pname
resetReadyStatus(false);
resetDataHash();
removeWildcards((char*)sPlayer);
if (!hostCampaign((char*)game.name,(char*)sPlayer))
{
addConsoleMessage(_("Sorry! Failed to host the game."), DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
break;
}
bHosted = true;
loadMapSettings2();
2013-10-31 19:43:00 -07:00
widgDelete(psWScreen, MULTIOP_REFRESH);
widgDelete(psWScreen, MULTIOP_HOST);
widgDelete(psWScreen, MULTIOP_FILTER_TOGGLE);
ingame.localOptionsReceived = true;
addGameOptions(); // update game options box.
addChatBox();
disableMultiButs();
addPlayerBox(!ingame.bHostSetup || bHosted); //to make sure host can't skip player selection menu (sets game.skdiff to UBYTE_MAX for humans)
break;
case MULTIOP_CHATEDIT:
// don't send empty lines to other players in the lobby
if(!strcmp(widgGetString(psWScreen, MULTIOP_CHATEDIT), ""))
break;
sendTextMessage(widgGetString(psWScreen, MULTIOP_CHATEDIT),true); //send
widgSetString(psWScreen, MULTIOP_CHATEDIT, ""); // clear box
break;
case CON_CANCEL:
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
if (!challengeActive)
{
NETGameLocked(false); // reset status on a cancel
stopJoining();
}
else
{
NETclose();
bHosted = false;
ingame.localJoiningInProgress = false;
changeTitleMode(SINGLE);
addChallenges();
}
break;
case MULTIOP_MAP_PREVIEW:
loadMapPreview(true);
break;
case MULTIOP_AI_CLOSED:
NetPlay.players[aiChooserUp].ai = AI_CLOSED;
break;
case MULTIOP_AI_OPEN:
NetPlay.players[aiChooserUp].ai = AI_OPEN;
break;
default:
break;
}
if (id == MULTIOP_AI_CLOSED || id == MULTIOP_AI_OPEN) // common code
{
game.skDiff[aiChooserUp] = 0; // disable AI for this slot
NETBroadcastPlayerInfo(aiChooserUp);
closeAiChooser();
addPlayerBox(!ingame.bHostSetup || bHosted);
}
2012-12-09 13:32:10 -08:00
if (id >= MULTIOP_DIFFICULTY_CHOOSE_START && id <= MULTIOP_DIFFICULTY_CHOOSE_END && difficultyChooserUp != -1)
{
int idx = id - MULTIOP_DIFFICULTY_CHOOSE_START;
NetPlay.players[difficultyChooserUp].difficulty = idx;
game.skDiff[difficultyChooserUp] = difficultyValue[idx];
NETBroadcastPlayerInfo(difficultyChooserUp);
closeDifficultyChooser();
addPlayerBox(!ingame.bHostSetup || bHosted);
}
2012-12-09 13:32:10 -08:00
if (id >= MULTIOP_AI_START && id <= MULTIOP_AI_END && aiChooserUp != -1)
{
int idx = id - MULTIOP_AI_START;
NetPlay.players[aiChooserUp].ai = idx;
sstrcpy(NetPlay.players[aiChooserUp].name, getAIName(aiChooserUp));
game.skDiff[aiChooserUp] = difficultyValue[NetPlay.players[aiChooserUp].difficulty]; // set difficulty, in case re-enabled
NETBroadcastPlayerInfo(aiChooserUp);
closeAiChooser();
addPlayerBox(!ingame.bHostSetup || bHosted);
}
2011-01-03 05:48:41 -08:00
STATIC_ASSERT(MULTIOP_TEAMS_START + MAX_PLAYERS - 1 <= MULTIOP_TEAMS_END);
if (id >= MULTIOP_TEAMS_START && id <= MULTIOP_TEAMS_START + MAX_PLAYERS - 1 && !locked.teams) // Clicked on a team chooser
{
int clickedMenuID = id - MULTIOP_TEAMS_START;
//make sure team chooser is not up before adding new one for another player
if (teamChooserUp < 0 && colourChooserUp < 0 && canChooseTeamFor(clickedMenuID) && positionChooserUp < 0)
{
addTeamChooser(clickedMenuID);
}
}
//clicked on a team
2011-01-03 05:48:41 -08:00
STATIC_ASSERT(MULTIOP_TEAMCHOOSER + MAX_PLAYERS - 1 <= MULTIOP_TEAMCHOOSER_END);
if(id >= MULTIOP_TEAMCHOOSER && id <= MULTIOP_TEAMCHOOSER + MAX_PLAYERS - 1)
{
ASSERT(teamChooserUp >= 0, "teamChooserUp < 0");
ASSERT(id >= MULTIOP_TEAMCHOOSER
&& (id - MULTIOP_TEAMCHOOSER) < MAX_PLAYERS, "processMultiopWidgets: wrong id - MULTIOP_TEAMCHOOSER value (%d)", id - MULTIOP_TEAMCHOOSER);
resetReadyStatus(false); // will reset only locally if not a host
SendTeamRequest(teamChooserUp, (UBYTE)id - MULTIOP_TEAMCHOOSER);
debug(LOG_WZ, "Changed team for player %d to %d", teamChooserUp, NetPlay.players[teamChooserUp].team);
closeTeamChooser();
addPlayerBox( !ingame.bHostSetup || bHosted); //restore initial options screen
}
// 'ready' button
2011-01-03 05:48:41 -08:00
if(id >= MULTIOP_READY_START && id <= MULTIOP_READY_END) // clicked on a player
{
UBYTE player = (UBYTE)(id-MULTIOP_READY_START);
if (player == selectedPlayer && teamChooserUp < 0 && positionChooserUp < 0)
{
SendReadyRequest(selectedPlayer, !NetPlay.players[player].ready);
// if hosting try to start the game if everyone is ready
if(NetPlay.isHost && multiplayPlayersReady(false))
{
startMultiplayerGame();
// reset flag in case people dropped/quit on join screen
NETsetPlayerConnectionStatus(CONNECTIONSTATUS_NORMAL, NET_ALL_PLAYERS);
}
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (NetPlay.isHost && !alliancesSetTeamsBeforeGame(game.alliance))
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
{
if(mouseDown(MOUSE_RMB) && player != NetPlay.hostPlayer) // both buttons....
{
char *msg;
sasprintf(&msg, _("The host has kicked %s from the game!"), getPlayerName(player));
sendTextMessage(msg, true);
kickPlayer(player, "you are unwanted by the host.", ERROR_KICKED);
resetReadyStatus(true); //reset and send notification to all clients
}
}
}
if (id >= MULTIOP_COLOUR_START && id <= MULTIOP_COLOUR_END && (id - MULTIOP_COLOUR_START == selectedPlayer || NetPlay.isHost))
{
if (teamChooserUp < 0 && positionChooserUp < 0 && colourChooserUp < 0) // not choosing something else already
{
addColourChooser(id - MULTIOP_COLOUR_START);
}
}
// clicked on a player
2011-01-03 05:48:41 -08:00
STATIC_ASSERT(MULTIOP_PLAYER_START + MAX_PLAYERS - 1 <= MULTIOP_PLAYER_END);
if (id >= MULTIOP_PLAYER_START && id <= MULTIOP_PLAYER_START + MAX_PLAYERS - 1
&& !locked.position
&& (id - MULTIOP_PLAYER_START == selectedPlayer || NetPlay.isHost
|| (positionChooserUp >= 0 && !isHumanPlayer(id - MULTIOP_PLAYER_START))))
{
int player = id - MULTIOP_PLAYER_START;
if ((player == selectedPlayer || (NetPlay.players[player].allocated && NetPlay.isHost))
&& positionChooserUp < 0 && teamChooserUp < 0 && colourChooserUp < 0)
{
addPositionChooser(player);
}
else if (positionChooserUp == player)
{
closePositionChooser(); // changed his mind
addPlayerBox(!ingame.bHostSetup || bHosted);
}
else if (positionChooserUp >= 0)
{
// Switch player
resetReadyStatus(false); // will reset only locally if not a host
SendPositionRequest(positionChooserUp, NetPlay.players[player].position);
closePositionChooser();
addPlayerBox(!ingame.bHostSetup || bHosted);
}
else if (!NetPlay.players[player].allocated && !locked.ai && NetPlay.isHost
&& positionChooserUp < 0 && teamChooserUp < 0 && colourChooserUp < 0)
2011-01-10 10:26:30 -08:00
{
addAiChooser(player);
}
}
if (id >= MULTIOP_DIFFICULTY_INIT_START && id <= MULTIOP_DIFFICULTY_INIT_END
&& !locked.difficulty && NetPlay.isHost && positionChooserUp < 0 && teamChooserUp < 0 && colourChooserUp < 0)
{
addDifficultyChooser(id - MULTIOP_DIFFICULTY_INIT_START);
addPlayerBox(!ingame.bHostSetup || bHosted);
}
2011-01-03 05:48:41 -08:00
STATIC_ASSERT(MULTIOP_COLCHOOSER + MAX_PLAYERS - 1 <= MULTIOP_COLCHOOSER_END);
if (id >= MULTIOP_COLCHOOSER && id < MULTIOP_COLCHOOSER + MAX_PLAYERS - 1) // chose a new colour.
{
resetReadyStatus(false); // will reset only locally if not a host
SendColourRequest(colourChooserUp, id - MULTIOP_COLCHOOSER);
closeColourChooser();
addPlayerBox(!ingame.bHostSetup || bHosted);
}
if (id == MULTIOP_TEAMCHOOSER_KICK)
{
char *msg;
sasprintf(&msg, _("The host has kicked %s from the game!"), getPlayerName(teamChooserUp));
kickPlayer(teamChooserUp, "you are unwanted by the host.", ERROR_KICKED);
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
sendTextMessage(msg, true);
resetReadyStatus(true); //reset and send notification to all clients
closeTeamChooser();
}
}
/* Start a multiplayer or skirmish game */
void startMultiplayerGame(void)
{
if (!bHosted)
{
debug(LOG_ERROR, "Multiple start requests received when we already started.");
return;
}
decideWRF(); // set up swrf & game.map
bMultiPlayer = true;
bMultiMessages = true;
NETsetPlayerConnectionStatus(CONNECTIONSTATUS_NORMAL, NET_ALL_PLAYERS); // reset disconnect conditions
initLoadingScreen(true);
if (NetPlay.isHost)
{
// This sets the limits to whatever the defaults are for the limiter screen
// If host sets limits, then we do not need to do the following routine.
if (!bLimiterLoaded)
{
debug(LOG_NET, "limiter was NOT activated, setting defaults");
if (!resLoad("wrf/limiter_data.wrf", 503))
{
debug(LOG_INFO, "Unable to load limiter_data.");
}
}
else
{
debug(LOG_NET, "limiter was activated");
}
resetDataHash(); // need to reset it, since host's data has changed.
createLimitSet();
debug(LOG_NET,"sending our options to all clients");
sendOptions();
NEThaltJoining(); // stop new players entering.
ingame.TimeEveryoneIsInGame = 0;
ingame.isAllPlayersDataOK = false;
memset(&ingame.DataIntegrity, 0x0, sizeof(ingame.DataIntegrity)); //clear all player's array
SendFireUp(); //bcast a fireup message
}
debug(LOG_NET, "title mode STARTGAME is set--Starting Game!");
changeTitleMode(STARTGAME);
bHosted = false;
if (NetPlay.isHost)
{
sendTextMessage(_("Host is Starting Game"),true);
}
}
// ////////////////////////////////////////////////////////////////////////////
// Net message handling
void frontendMultiMessages(void)
{
NETQUEUE queue;
uint8_t type;
while (NETrecvNet(&queue, &type))
{
// Copy the message to the global one used by the new NET API
switch(type)
{
case NET_FILE_REQUESTED:
recvMapFileRequested(queue);
break;
case NET_FILE_PAYLOAD:
{
bool done = recvMapFileData(queue);
((MultibuttonWidget *)widgGetFromID(psWScreen, MULTIOP_MAP_PREVIEW))->enable(done); // turn preview button on or off
break;
}
case NET_FILE_CANCELLED: // host only routine
{
uint32_t reason;
uint32_t victim;
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if(!NetPlay.isHost) // only host should act
{
ASSERT(false, "Host only routine detected for client!");
break;
}
NETbeginDecode(queue, NET_FILE_CANCELLED);
NETuint32_t(&victim);
NETuint32_t(&reason);
NETend();
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(victim) != queue.index)
{
HandleBadParam("NET_FILE_CANCELLED given incorrect params.", victim, queue.index);
return;
}
switch (reason)
{
case STUCK_IN_FILE_LOOP:
debug(LOG_WARNING, "Received file cancel request from player %u, They are stuck in a loop?", victim);
kickPlayer(victim, "the host couldn't send a file for some reason.", ERROR_UNKNOWNFILEISSUE);
NetPlay.players[victim].wzFile.isCancelled = true;
NetPlay.players[victim].wzFile.isSending = false;
break;
case ALREADY_HAVE_FILE:
default:
debug(LOG_WARNING, "Received file cancel request from player %u, they already have the file.", victim);
NetPlay.players[victim].wzFile.isCancelled = true;
NetPlay.players[victim].wzFile.isSending = false;
break;
}
}
break;
case NET_OPTIONS: // incoming options file.
recvOptions(queue);
ingame.localOptionsReceived = true;
if(titleMode == MULTIOPTION)
{
addGameOptions();
disableMultiButs();
addChatBox();
}
break;
case GAME_ALLIANCE:
recvAlliance(queue, false);
break;
case NET_COLOURREQUEST:
recvColourRequest(queue);
break;
case NET_POSITIONREQUEST:
recvPositionRequest(queue);
break;
case NET_TEAMREQUEST:
recvTeamRequest(queue);
break;
case NET_READY_REQUEST:
recvReadyRequest(queue);
// If hosting and game not yet started, try to start the game if everyone is ready.
if (NetPlay.isHost && bHosted && multiplayPlayersReady(false))
{
startMultiplayerGame();
}
break;
case NET_PING: // diagnostic ping msg.
recvPing(queue);
break;
case NET_PLAYER_DROPPED: // remote player got disconnected
{
uint32_t player_id = MAX_PLAYERS;
resetReadyStatus(false);
NETbeginDecode(queue, NET_PLAYER_DROPPED);
{
NETuint32_t(&player_id);
}
NETend();
if (player_id >= MAX_PLAYERS)
{
debug(LOG_INFO, "** player %u has dropped - huh?", player_id);
break;
}
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (whosResponsible(player_id) != queue.index && queue.index != NET_HOST_ONLY)
{
HandleBadParam("NET_PLAYER_DROPPED given incorrect params.", player_id, queue.index);
break;
}
debug(LOG_INFO,"** player %u has dropped!", player_id);
MultiPlayerLeave(player_id); // get rid of their stuff
NET_InitPlayer(player_id, false); // sets index player's array to false
NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, player_id);
if (player_id == NetPlay.hostPlayer || player_id == selectedPlayer) // if host quits or we quit, abort out
{
stopJoining();
}
break;
}
case NET_PLAYERRESPONDING: // remote player is now playing.
{
uint32_t player_id;
resetReadyStatus(false);
NETbeginDecode(queue, NET_PLAYERRESPONDING);
// the player that has just responded
NETuint32_t(&player_id);
NETend();
ingame.JoiningInProgress[player_id] = false;
ingame.DataIntegrity[player_id] = false;
break;
}
case NET_FIREUP: // campaign game started.. can fire the whole shebang up...
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (NET_HOST_ONLY != queue.index)
{
HandleBadParam("NET_FIREUP given incorrect params.", 255, queue.index);
break;
}
debug(LOG_NET, "NET_FIREUP was received ...");
if(ingame.localOptionsReceived)
{
uint32_t randomSeed = 0;
NETbeginDecode(queue, NET_FIREUP);
NETuint32_t(&randomSeed);
NETend();
gameSRand(randomSeed); // Set the seed for the synchronised random number generator, using the seed given by the host.
debug(LOG_NET, "& local Options Received (MP game)");
ingame.TimeEveryoneIsInGame = 0; // reset time
resetDataHash();
decideWRF();
bMultiPlayer = true;
bMultiMessages = true;
changeTitleMode(STARTGAME);
bHosted = false;
// Start the game before processing more messages.
NETpop(queue);
return;
}
ASSERT(false, "NET_FIREUP was received, but !ingame.localOptionsReceived.");
break;
case NET_KICK: // player is forcing someone to leave
{
uint32_t player_id;
char reason[MAX_KICK_REASON];
LOBBY_ERROR_TYPES KICK_TYPE = ERROR_NOERROR;
NETbeginDecode(queue, NET_KICK);
NETuint32_t(&player_id);
NETstring(reason, MAX_KICK_REASON);
NETenum(&KICK_TYPE);
NETend();
if (player_id == NET_HOST_ONLY)
{
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
char buf[250]= {'\0'};
ssprintf(buf, "*Player %d (%s : %s) tried to kick %u", (int) queue.index, NetPlay.players[queue.index].name, NetPlay.players[queue.index].IPtextAddress, player_id);
NETlogEntry(buf, SYNC_FLAG, 0);
debug(LOG_ERROR, "%s", buf);
if (NetPlay.isHost)
{
NETplayerKicked((unsigned int) queue.index);
}
break;
}
if (selectedPlayer == player_id) // we've been told to leave.
{
setLobbyError(KICK_TYPE);
stopJoining();
//screen_RestartBackDrop();
changeTitleMode(GAMEFIND);
pie_LoadBackDrop(SCREEN_RANDOMBDROP);
debug(LOG_ERROR, "You have been kicked, because %s ", reason);
}
else
{
NETplayerKicked(player_id);
}
break;
}
case NET_HOST_DROPPED:
NETbeginDecode(queue, NET_HOST_DROPPED);
NETend();
stopJoining();
debug(LOG_NET, "The host has quit!");
setLobbyError(ERROR_HOSTDROPPED);
changeTitleMode(GAMEFIND);
break;
case NET_TEXTMSG: // Chat message
if(ingame.localOptionsReceived)
{
recvTextMessage(queue);
}
break;
default:
debug(LOG_ERROR, "Didn't handle %s message!", messageTypeToString(type));
break;
}
NETpop(queue);
}
}
void runMultiOptions(void)
{
static UDWORD lastrefresh = 0;
char oldGameMap[128];
2011-12-04 02:09:48 -08:00
int oldMaxPlayers;
PLAYERSTATS playerStats;
W_CONTEXT context;
KEY_CODE k;
char str[3];
frontendMultiMessages();
if (NetPlay.isHost)
{
for (unsigned i = 0; i < MAX_PLAYERS; i++)
{
// send it for each player that needs it
if (NetPlay.players[i].wzFile.isSending)
{
sendMap();
}
}
}
// update boxes?
if (netPlayersUpdated || (NetPlay.isHost && mouseDown(MOUSE_LMB) && gameTime-lastrefresh>500))
{
netPlayersUpdated = false;
lastrefresh= gameTime;
if (!multiRequestUp && (bHosted || ingame.localJoiningInProgress))
{
addPlayerBox(true); // update the player box.
loadMapPreview(false);
}
}
// update scores and pings if far enough into the game
if(ingame.localOptionsReceived && ingame.localJoiningInProgress)
{
sendScoreCheck();
sendPing();
}
// if typing and not in an edit box then jump to chat box.
k = getQwertyKey();
if( k && psWScreen->psFocus == NULL)
{
context.xOffset = 0;
context.yOffset = 0;
context.mx = mouseX();
context.my = mouseY();
keyScanToString(k,(char*)&str,3);
if(widgGetFromID(psWScreen,MULTIOP_CHATEDIT))
{
widgSetString(psWScreen, MULTIOP_CHATEDIT, (char*)&str); // start it up!
widgGetFromID(psWScreen, MULTIOP_CHATEDIT)->clicked(&context);
}
}
// chat box handling
if(widgGetFromID(psWScreen,MULTIOP_CHATBOX))
{
while(getNumberConsoleMessages() >getConsoleLineInfo())
{
removeTopConsoleMessage();
}
updateConsoleMessages(); // run the chatbox
}
// widget handling
if(multiRequestUp)
{
WidgetTriggers const &triggers = widgRunScreen(psRScreen);
unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu.
LEVEL_DATASET *mapData;
2011-12-04 02:09:48 -08:00
bool isHoverPreview;
QString sTemp;
if (runMultiRequester(id, &id, &sTemp, &mapData, &isHoverPreview))
{
Sha256 oldGameHash;
switch(id)
{
case MULTIOP_PNAME:
sstrcpy(sPlayer, sTemp.toUtf8().constData());
widgSetString(psWScreen, MULTIOP_PNAME, sTemp.toUtf8().constData());
removeWildcards((char*)sPlayer);
printConsoleNameChange(NetPlay.players[selectedPlayer].name, sPlayer);
NETchangePlayerName(selectedPlayer, (char*)sPlayer);
loadMultiStats((char*)sPlayer,&playerStats);
setMultiStats(selectedPlayer,playerStats,false);
setMultiStats(selectedPlayer,playerStats,true);
netPlayersUpdated = true;
break;
case MULTIOP_MAP:
{
2011-12-04 02:09:48 -08:00
sstrcpy(oldGameMap, game.map);
oldGameHash = game.hash;
2011-12-04 02:09:48 -08:00
oldMaxPlayers = game.maxPlayers;
sstrcpy(game.map, mapData->pName);
game.hash = levGetFileHash(mapData);
game.maxPlayers = mapData->players;
game.isMapMod = CheckForMod(mapData->realFileName);
2011-12-04 02:09:48 -08:00
loadMapPreview(!isHoverPreview);
if (isHoverPreview)
{
sstrcpy(game.map, oldGameMap);
game.hash = oldGameHash;
2011-12-04 02:09:48 -08:00
game.maxPlayers = oldMaxPlayers;
}
else
{
loadMapSettings1();
}
widgSetString(psWScreen, MULTIOP_MAP+1, mapData->pName); //What a horrible, horrible way to do this! FIX ME! (See addBlueForm)
addGameOptions();
break;
}
default:
2011-12-04 02:09:48 -08:00
loadMapPreview(false); // Restore the preview of the old map.
break;
}
2011-12-04 02:09:48 -08:00
if (!isHoverPreview)
{
addPlayerBox( !ingame.bHostSetup );
}
}
}
else
{
if(hideTime != 0)
{
// we abort the 'hidetime' on press of a mouse button.
if(gameTime-hideTime < MAP_PREVIEW_DISPLAY_TIME && !mousePressed(MOUSE_LMB) && !mousePressed(MOUSE_RMB))
{
return;
}
inputLoseFocus(); // remove the mousepress from the input stream.
hideTime = 0;
}
WidgetTriggers const &triggers = widgRunScreen(psWScreen);
unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu.
processMultiopWidgets(id);
}
widgDisplayScreen(psWScreen); // show the widgets currently running
if(multiRequestUp)
{
widgDisplayScreen(psRScreen); // show the Requester running
}
if(widgGetFromID(psWScreen,MULTIOP_CHATBOX))
{
iV_SetFont(font_regular); // switch to small font.
displayConsoleMessages(); // draw the chatbox
}
if (CancelPressed())
{
processMultiopWidgets(CON_CANCEL); // "Press" the cancel button to clean up net connections and stuff.
}
if (!NetPlay.isHostAlive && !ingame.bHostSetup)
{
changeTitleMode(GAMEFIND);
screen_RestartBackDrop();
}
}
bool startMultiOptions(bool bReenter)
{
PLAYERSTATS nullStats;
UBYTE i;
netPlayersUpdated = true;
addBackdrop();
loadMapPreview(false);
addTopForm();
Let the host kick people in non alliance games. (left+right click over ready button of the player you want kicked) close ticket:3100 close ticket:3121 Fix kick routines that got clobbered by the merge a long time ago, and also implement a in-game kick. Currently, you hold down the right mouse button over the channel icon of the person you want to kick, and it will kick them. There is no more room for a new icon. :( git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11490 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit da30725d967cf7076456f56122b28ddbc3202488) Conflicts: src/multiint.cpp src/multimenu.cpp src/multiplay.cpp Allow the host to kick the AI only in 'cheat mode' in skirmish games. Closes ticket:2139 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11596 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 7a083b154dab6b6fa8f421042db0670a671127f5) Conflicts: src/multimenu.cpp When kicking, make sure they are an actual human player before adding them to the list. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11506 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 43b180768c71853b3de97d71fa091e6a0fdfc10e) Conflicts: lib/netplay/netplay.c general networking improvements by popular request. git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11568 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 052d03d19ec967d4b8cee5d79446fe5ec1f2b040) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multijoin.cpp src/multiplay.cpp src/multiplay.h src/multisync.c Fix client side messages. refs ticket:2144 git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/branches/2.3@11601 4a71c877-e1ca-e34f-864e-861f7616d084 (cherry picked from commit 98723c00a9b7d6a46eab204ccd474df4a62fbd93) Conflicts: lib/netplay/netplay.c src/multiint.cpp src/multiplay.cpp
2012-02-19 14:49:49 -08:00
if (getLobbyError() != ERROR_INVALID)
{
setLobbyError(ERROR_NOERROR);
}
// free limiter structure
if(!bReenter || challengeActive)
{
if(ingame.numStructureLimits)
{
ingame.numStructureLimits = 0;
free(ingame.pStructureLimits);
ingame.pStructureLimits = NULL;
}
}
if(!bReenter)
{
memset(nameOverrides, 0, sizeof(nameOverrides));
memset(&locked, 0, sizeof(locked)); // nothing is locked by default
teamChooserUp = -1;
aiChooserUp = -1;
difficultyChooserUp = -1;
positionChooserUp = -1;
colourChooserUp = -1;
for(i=0; i < MAX_PLAYERS; i++)
{
game.skDiff[i] = (DIFF_SLIDER_STOPS / 2); // reset AI (turn it on again)
setPlayerColour(i,i); //reset all colors as well
}
game.isMapMod = false; // reset map-mod status
game.mapHasScavengers = true; // FIXME, should default to false
if(!NetPlay.bComms) // force skirmish if no comms.
{
game.type = SKIRMISH;
sstrcpy(game.map, DEFAULTSKIRMISHMAP);
game.hash = levGetMapNameHash(game.map);
game.maxPlayers = 4;
}
ingame.localOptionsReceived = false;
loadMultiStats((char*)sPlayer,&nullStats);
}
if (!bReenter && challengeActive)
{
resetReadyStatus(false);
removeWildcards((char*)sPlayer);
if (!hostCampaign((char*)game.name,(char*)sPlayer))
{
debug(LOG_ERROR, "Failed to host the challenge.");
return false;
}
bHosted = true;
loadMapSettings1();
loadMapSettings2();
WzConfig ini(sRequestResult, WzConfig::ReadOnly);
ini.beginGroup("challenge");
sstrcpy(game.map, ini.value("Map", game.map).toString().toUtf8().constData());
game.hash = levGetMapNameHash(game.map);
game.maxPlayers = ini.value("MaxPlayers", game.maxPlayers).toInt(); // TODO, read from map itself, not here!!
game.scavengers = ini.value("Scavengers", game.scavengers).toBool();
game.alliance = ALLIANCES_TEAMS;
netPlayersUpdated = true;
mapDownloadProgress = 100;
game.power = ini.value("powerLevel", game.power).toInt();
game.base = ini.value("Bases", game.base + 1).toInt() - 1; // count from 1 like the humans do
sstrcpy(game.name, ini.value("name").toString().toUtf8().constData());
locked.position = ini.value("AllowPositionChange", locked.position).toBool();
ini.endGroup();
ingame.localOptionsReceived = true;
addGameOptions(); // update game options box.
addChatBox();
disableMultiButs();
addPlayerBox(true);
}
else
{
addPlayerBox(false); // Players
addGameOptions();
addChatBox();
2013-10-31 12:35:09 -07:00
if (ingame.bHostSetup)
{
2013-10-31 12:35:09 -07:00
char buf[512] = {'\0'};
if (NetPlay.bComms)
{
2013-10-31 12:35:09 -07:00
if (NetPlay.isUPNP)
{
if (NetPlay.isUPNP_CONFIGURED)
{
ssprintf(buf, _("UPnP has been enabled."));
}
else
{
if (NetPlay.isUPNP_ERROR)
{
ssprintf(buf, _("UPnP detection faled. You must manually configure router yourself."));
}
else
{
ssprintf(buf, _("UPnP detection is in progress..."));
}
}
2013-10-31 12:35:09 -07:00
addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
}
else
{
ssprintf(buf, _("UPnP detection disabled by user. Autoconfig of port 2100 will not happen."));
addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
}
}
if (challengeActive)
{
ssprintf(buf, _("Hit the ready box to begin your challenge!"));
}
else
{
ssprintf(buf, _("Press the start hosting button to begin hosting a game."));
}
2013-10-31 12:35:09 -07:00
addConsoleMessage(buf, DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
}
}
// going back to multiop after setting limits up..
if(bReenter && bHosted)
{
disableMultiButs();
}
loadMapPreview(false);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Drawing functions
void displayChatEdit(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y() - 4; // 4 is the magic number.
// draws the line at the bottom of the multiplayer join dialog separating the chat
// box from the input box
iV_Line(x, y, x + psWidget->width(), y, WZCOL_MENU_SEPARATOR);
return;
}
// ////////////////////////////////////////////////////////////////////////////
void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y();
UDWORD gameID = psWidget->UserData;
char tmp[80], name[StringSize];
if ( (LobbyError != ERROR_NOERROR) && (bMultiPlayer && !NetPlay.bComms))
{
addConsoleMessage(_("Can't connect to lobby server!"), DEFAULT_JUSTIFY, NOTIFY_MESSAGE);
return;
}
// Draw blue boxes for games (buttons) & headers
drawBlueBox(x, y, psWidget->width(), psWidget->height());
drawBlueBox(x, y, GAMES_STATUS_START - 4 ,psWidget->height());
drawBlueBox(x, y, GAMES_PLAYERS_START - 4 ,psWidget->height());
drawBlueBox(x, y, GAMES_MAPNAME_START - 4, psWidget->height());
//draw game info
iV_SetFont(font_regular);
int lamp = IMAGE_LAMP_RED;
int statusStart = IMAGE_NOJOIN;
bool disableButton = true;
iV_SetTextColour(WZCOL_TEXT_DARK);
// As long as they got room, and mods are the same then we proccess the button(s)
if (NETisCorrectVersion(NetPlay.games[gameID].game_version_major, NetPlay.games[gameID].game_version_minor))
{
if (strcmp(NetPlay.games[gameID].modlist, getModList()) != 0)
{
// If wrong mod loaded.
statusStart = IMAGE_NOJOIN_MOD;
}
else if (NetPlay.games[gameID].desc.dwCurrentPlayers >= NetPlay.games[gameID].desc.dwMaxPlayers)
{
// If game is full.
statusStart = IMAGE_NOJOIN_FULL;
}
else
{
// Game is ok to join!
iV_SetTextColour(WZCOL_FORM_TEXT);
lamp = IMAGE_LAMP_GREEN;
statusStart = IMAGE_SKIRMISH_OVER;
disableButton = false;
if (NetPlay.games[gameID].privateGame) // check to see if it is a private game
{
statusStart = IMAGE_LOCKED_NOBG;
lamp = IMAGE_LAMP_AMBER;
}
}
ssprintf(tmp, "%d/%d", NetPlay.games[gameID].desc.dwCurrentPlayers, NetPlay.games[gameID].desc.dwMaxPlayers);
iV_DrawText(tmp, x + GAMES_PLAYERS_START + 4 , y + 18);
// see what host limits are... then draw them.
if (NetPlay.games[gameID].limits)
{
if(NetPlay.games[gameID].limits & NO_TANKS)
iV_DrawImage(FrontImages, IMAGE_NO_TANK, x + GAMES_STATUS_START + 37, y + 2);
if(NetPlay.games[gameID].limits & NO_BORGS)
iV_DrawImage(FrontImages, IMAGE_NO_CYBORG, x + GAMES_STATUS_START + (37 * 2), y + 2);
if(NetPlay.games[gameID].limits & NO_VTOLS)
iV_DrawImage(FrontImages, IMAGE_NO_VTOL, x + GAMES_STATUS_START + (37 * 3) , y + 2);
}
}
// Draw type overlay.
iV_DrawImage(FrontImages, statusStart, x + GAMES_STATUS_START, y + 3);
iV_DrawImage(FrontImages, lamp, x - 14, y + 8);
if (disableButton)
{
widgSetButtonState(psWScreen, psWidget->id, WBUT_DISABLE);
}
//draw game name, chop when we get a too long name
sstrcpy(name, NetPlay.games[gameID].name);
// box size in pixels
while (iV_GetTextWidth(name) > (GAMES_MAPNAME_START - GAMES_GAMENAME_START- 4) )
{
name[strlen(name)-1]='\0';
}
iV_DrawText(name, x + GAMES_GAMENAME_START, y + 12);
if (NetPlay.games[gameID].pureMap)
{
iV_SetTextColour(WZCOL_RED);
}
else
{
iV_SetTextColour(WZCOL_FORM_TEXT);
}
// draw map name, chop when we get a too long name
sstrcpy(name, NetPlay.games[gameID].mapname);
// box size in pixels
while (iV_GetTextWidth(name) > (GAMES_PLAYERS_START - GAMES_MAPNAME_START - 4) )
{
name[strlen(name)-1]='\0';
}
iV_DrawText(name, x + GAMES_MAPNAME_START, y + 12); // map name
iV_SetTextColour(WZCOL_FORM_TEXT);
iV_SetFont(font_small);
// draw mod name (if any)
if (strlen(NetPlay.games[gameID].modlist))
{
// FIXME: we really don't have enough space to list all mods
char tmp[300];
sprintf(tmp, _("Mods: %s"), NetPlay.games[gameID].modlist);
tmp[StringSize] = '\0';
sstrcpy(name, tmp);
}
else
{
sstrcpy(name, _("Mods: None!"));
}
// box size in pixels
while (iV_GetTextWidth(name) > (GAMES_PLAYERS_START - GAMES_MAPNAME_START - 8) )
{
name[strlen(name)-1]='\0';
}
iV_DrawText(name, x + GAMES_MODNAME_START, y + 24 );
// draw version string
sprintf(name, _("Version: %s"), NetPlay.games[gameID].versionstring);
// box size in pixels
while (iV_GetTextWidth(name) > (GAMES_MAPNAME_START - 6 - GAMES_GAMENAME_START - 4) )
{
name[strlen(name)-1]='\0';
}
iV_DrawText(name, x + GAMES_GAMENAME_START + 6, y + 24 );
// crappy hack to only draw this once for the header. TODO fix GUI
if (gameID == 0)
{
iV_SetTextColour(WZCOL_YELLOW);
// make the 'header' for the table...
drawBlueBox(x , y - 12 , GAMES_GAMEWIDTH, 12);
ssprintf(tmp, "Game Name");
iV_DrawText(tmp, x - 2 + GAMES_GAMENAME_START + 48, y - 3);
ssprintf(tmp, "Map Name");
iV_DrawText(tmp, x - 2 + GAMES_MAPNAME_START + 48, y - 3);
ssprintf(tmp, "Players");
iV_DrawText(tmp, x - 2 + GAMES_PLAYERS_START, y - 3);
ssprintf(tmp, "Status");
iV_DrawText(tmp, x - 2 + GAMES_STATUS_START + 48, y - 3);
}
}
void displayTeamChooser(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y();
UDWORD i = psWidget->UserData;
ASSERT(i < MAX_PLAYERS && NetPlay.players[i].team >= 0 && NetPlay.players[i].team < MAX_PLAYERS, "Team index out of bounds" );
drawBlueBox(x, y, psWidget->width(), psWidget->height());
if (game.skDiff[i])
{
iV_DrawImage(FrontImages, IMAGE_TEAM0 + NetPlay.players[i].team, x + 2, y + 8);
}
}
void displayPosition(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
const int x = xOffset + psWidget->x();
const int y = yOffset + psWidget->y();
const int i = psWidget->UserData;
char text[80];
drawBlueBox(x, y, psWidget->width(), psWidget->height());
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_FORM_TEXT);
ssprintf(text, _("Click to take player slot %d"), NetPlay.players[i].position);
iV_DrawText(text, x + 10, y + 22);
}
static void displayAi(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
const int x = xOffset + psWidget->x();
const int y = yOffset + psWidget->y();
const int j = psWidget->UserData;
const char *commsText[] = { N_("Open"), N_("Closed") };
drawBlueBox(x, y, psWidget->width(), psWidget->height());
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_FORM_TEXT);
iV_DrawText((j >= 0) ? aidata[j].name : gettext(commsText[j + 2]), x + 10, y + 22);
}
static int difficultyIcon(int difficulty)
{
switch (difficulty)
{
case 0: return IMAGE_EASY;
case 1: return IMAGE_MEDIUM;
case 2: return IMAGE_HARD;
case 3: return IMAGE_INSANE;
default: return IMAGE_NO; /// what??
}
}
static void displayDifficulty(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
const int x = xOffset + psWidget->x();
const int y = yOffset + psWidget->y();
const int j = psWidget->UserData;
ASSERT_OR_RETURN(, j < ARRAY_SIZE(difficultyList), "Bad difficulty found: %d", j);
drawBlueBox(x, y, psWidget->width(), psWidget->height());
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_FORM_TEXT);
iV_DrawImage(FrontImages, difficultyIcon(j), x + 5, y + 5);
iV_DrawText(gettext(difficultyList[j]), x + 42, y + 22);
}
static bool isKnownPlayer(std::map<std::string, EcKey::Key> const &knownPlayers, std::string const &name, EcKey const &key)
{
if (key.empty())
{
return false;
}
std::map<std::string, EcKey::Key>::const_iterator i = knownPlayers.find(name);
return i != knownPlayers.end() && key.toBytes(EcKey::Public) == i->second;
}
// ////////////////////////////////////////////////////////////////////////////
void displayPlayer(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y();
UDWORD j = psWidget->UserData, eval;
2011-01-18 14:41:09 -08:00
const int nameX = 32;
//bluboxes.
drawBlueBox(x, y, psWidget->width(), psWidget->height());
if (NetPlay.isHost && NetPlay.players[j].wzFile.isSending)
{
static char mapProgressString[MAX_STR_LENGTH] = {'\0'};
int progress = (NetPlay.players[j].wzFile.currPos * 100) / NetPlay.players[j].wzFile.fileSize_32;
snprintf(mapProgressString, MAX_STR_LENGTH, _("Sending Map: %d%% "), progress);
iV_SetFont(font_regular); // font
iV_SetTextColour(WZCOL_FORM_TEXT);
iV_DrawText(mapProgressString, x + 15, y + 22);
}
else if (mapDownloadProgress != 100 && j == selectedPlayer)
{
static char mapProgressString[MAX_STR_LENGTH] = {'\0'};
snprintf(mapProgressString, MAX_STR_LENGTH, _("Map: %d%% downloaded"), mapDownloadProgress);
iV_SetFont(font_regular); // font
iV_SetTextColour(WZCOL_FORM_TEXT);
iV_DrawText(mapProgressString, x + 5, y + 22);
return;
}
else if (ingame.localOptionsReceived && NetPlay.players[j].allocated) // only draw if real player!
{
std::string name = NetPlay.players[j].name;
drawBlueBox(x, y, psWidget->width(), psWidget->height());
iV_SetFont(font_regular); // font
std::map<std::string, EcKey::Key> serverPlayers; // TODO Fill this with players known to the server (needs implementing on the server, too). Currently useless.
if (ingame.PingTimes[j] >= PING_LIMIT)
{
iV_SetTextColour(WZCOL_FORM_PLAYER_NOPING);
}
else if (isKnownPlayer(serverPlayers, name, getMultiStats(j).identity))
{
iV_SetTextColour(WZCOL_FORM_PLAYER_KNOWN_BY_SERVER);
}
else if (isKnownPlayer(getKnownPlayers(), name, getMultiStats(j).identity))
{
iV_SetTextColour(WZCOL_FORM_PLAYER_KNOWN);
}
else
{
iV_SetTextColour(WZCOL_FORM_PLAYER_UNKNOWN);
}
// name
if (iV_GetTextWidth(name.c_str()) > psWidget->width() - nameX)
{
while (!name.empty() && iV_GetTextWidth((name + "...").c_str()) > psWidget->width() - nameX)
2011-01-18 14:41:09 -08:00
{
name.resize(name.size() - 1); // Clip name.
}
name += "...";
}
std::string subText;
if (j == NET_HOST_ONLY && NetPlay.bComms)
{
subText += _("HOST");
}
if (NetPlay.bComms && j != selectedPlayer)
{
char buf[250] = {'\0'};
// show "actual" ping time
ssprintf(buf, "%s%s: ", subText.empty()? "" : ", ", _("Ping"));
subText += buf;
if (ingame.PingTimes[j] < PING_LIMIT)
{
ssprintf(buf, "%03d", ingame.PingTimes[j]);
}
else
{
ssprintf(buf, ""); // Player has ping of somewhat questionable quality.
}
subText += buf;
}
iV_DrawText(name.c_str(), x + nameX, y + (subText.empty()? 22 : 18));
if (!subText.empty())
{
iV_SetFont(font_small);
iV_SetTextColour(WZCOL_TEXT_MEDIUM);
iV_DrawText(subText.c_str(), x + nameX, y + 28);
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_FORM_TEXT);
}
PLAYERSTATS stat = getMultiStats(j);
if(stat.wins + stat.losses < 5)
{
iV_DrawImage(FrontImages, IMAGE_MEDAL_DUMMY, x + 4, y + 13);
}
else
{
PLAYERSTATS stat = getMultiStats(j);
// star 1 total droid kills
eval = stat.totalKills;
if(eval >600)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK1, x + 4, y + 3);
}
else if(eval >300)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK2, x + 4, y + 3);
}
else if(eval >150)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK3, x + 4, y + 3);
}
// star 2 games played (Cannot use stat.played, since that's just the number of times the player exited via the game menu, not the number of games played.)
eval = stat.wins + stat.losses;
if(eval >200)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK1, x + 4, y + 13);
}
else if(eval >100)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK2, x + 4, y + 13);
}
else if(eval >50)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK3, x + 4, y + 13);
}
// star 3 games won.
eval = stat.wins;
if(eval >80)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK1, x + 4, y + 23);
}
else if(eval >40)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK2, x + 4, y + 23);
}
else if(eval >10)
{
iV_DrawImage(FrontImages, IMAGE_MULTIRANK3, x + 4, y + 23);
}
// medals.
if ((stat.wins >= 6) && (stat.wins > (2 * stat.losses))) // bronze requirement.
{
if ((stat.wins >= 12) && (stat.wins > (4 * stat.losses))) // silver requirement.
{
if ((stat.wins >= 24) && (stat.wins > (8 * stat.losses))) // gold requirement
{
iV_DrawImage(FrontImages, IMAGE_MEDAL_GOLD, x + 16, y + 11);
}
else
{
iV_DrawImage(FrontImages, IMAGE_MEDAL_SILVER, x + 16, y + 11);
}
}
else
{
iV_DrawImage(FrontImages, IMAGE_MEDAL_BRONZE, x + 16, y + 11);
}
}
}
game.skDiff[j] = UBYTE_MAX; // set AI difficulty to 0xFF (i.e. not an AI)
}
else // AI
{
char aitext[80];
if (NetPlay.players[j].ai >= 0)
{
iV_DrawImage(FrontImages, IMAGE_PLAYER_PC, x, y + 11);
}
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_FORM_TEXT);
ASSERT_OR_RETURN(, NetPlay.players[j].ai < (int)aidata.size(), "Uh-oh, AI index out of bounds");
switch (NetPlay.players[j].ai)
{
case AI_OPEN: sstrcpy(aitext, _("Open")); break;
case AI_CLOSED: sstrcpy(aitext, _("Closed")); break;
default: sstrcpy(aitext, nameOverrides[j][0] == '\0'? NetPlay.isHost? aidata[NetPlay.players[j].ai].name : NetPlay.players[j].name : nameOverrides[j]); break;
}
2011-01-18 14:41:09 -08:00
iV_DrawText(aitext, x + nameX, y + 22);
}
}
void displayColour(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
const int x = xOffset + psWidget->x();
const int y = yOffset + psWidget->y();
const int j = psWidget->UserData;
drawBlueBox(x, y, psWidget->width(), psWidget->height());
if (!NetPlay.players[j].wzFile.isSending && game.skDiff[j])
{
int player = getPlayerColour(j);
STATIC_ASSERT(MAX_PLAYERS <= 16);
iV_DrawImageTc(FrontImages, IMAGE_PLAYERN, IMAGE_PLAYERN_TC, x + 7, y + 9, pal_GetTeamColour(player));
}
}
// ////////////////////////////////////////////////////////////////////////////
// Display blue box
void intDisplayFeBox(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y();
int w = psWidget->width();
int h = psWidget->height();
drawBlueBox(x,y,w,h);
}
// ////////////////////////////////////////////////////////////////////////////
// Display edit box
void displayMultiEditBox(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
{
int x = xOffset + psWidget->x();
int y = yOffset + psWidget->y();
drawBlueBox(x, y, psWidget->width(), psWidget->height());
if( ((W_EDITBOX*)psWidget)->state & WEDBS_DISABLE) // disabled
{
PIELIGHT colour;
colour.byte.r = FILLRED;
colour.byte.b = FILLBLUE;
colour.byte.g = FILLGREEN;
colour.byte.a = FILLTRANS;
pie_UniTransBoxFill(x, y, x + psWidget->width() + psWidget->height(), y + psWidget->height(), colour);
}
}
static Image getFrontHighlightImage(Image image)
{
if (image.isNull())
{
return Image();
}
switch (image.width())
{
case 30: return Image(FrontImages, IMAGE_HI34);
case 60: return Image(FrontImages, IMAGE_HI64);
case 19: return Image(FrontImages, IMAGE_HI23);
case 27: return Image(FrontImages, IMAGE_HI31);
case 35: return Image(FrontImages, IMAGE_HI39);
case 37: return Image(FrontImages, IMAGE_HI41);
case 56: return Image(FrontImages, IMAGE_HI56);
}
return Image();
}
void WzMultiButton::display(int xOffset, int yOffset)
{
int x0 = xOffset + x();
int y0 = yOffset + y();
Image hiToUse(NULL, 0);
// FIXME: This seems to be a way to conserve space, so you can use a
// transparent icon with these edit boxes.
// hack for multieditbox
if (imNormal.id == IMAGE_EDIT_MAP || imNormal.id == IMAGE_EDIT_GAME || imNormal.id == IMAGE_EDIT_PLAYER
|| imNormal.id == IMAGE_LOCK_BLUE || imNormal.id == IMAGE_UNLOCK_BLUE)
{
drawBlueBox(x0 - 2, y0 - 2, height(), height()); // box on end.
}
// evaluate auto-frame
bool highlight = (getState() & WBUT_HIGHLIGHT) != 0;
// evaluate auto-frame
if (doHighlight == 1 && highlight)
{
hiToUse = getFrontHighlightImage(imNormal);
}
bool down = (getState() & (WBUT_DOWN | WBUT_LOCK | WBUT_CLICKLOCK)) != 0;
bool grey = (getState() & WBUT_DISABLE) != 0;
Image toDraw[3];
int numToDraw = 0;
// now display
toDraw[numToDraw++] = imNormal;
// hilights etc..
if (down)
{
toDraw[numToDraw++] = imDown;
}
if (highlight && !grey && hiToUse.images != NULL)
{
toDraw[numToDraw++] = hiToUse;
}
for (int n = 0; n < numToDraw; ++n)
{
Image tcImage(toDraw[n].images, toDraw[n].id + 1);
if (tc == MAX_PLAYERS)
{
iV_DrawImage(toDraw[n], x0, y0);
}
else if (tc == MAX_PLAYERS + 1)
{
const int scale = 4000;
int f = realTime%scale;
PIELIGHT mix;
mix.byte.r = 128 + iSinR(65536*f/scale + 65536*0/3, 127);
mix.byte.g = 128 + iSinR(65536*f/scale + 65536*1/3, 127);
mix.byte.b = 128 + iSinR(65536*f/scale + 65536*2/3, 127);
mix.byte.a = 255;
iV_DrawImageTc(toDraw[n], tcImage, x0, y0, mix);
}
else
{
iV_DrawImageTc(toDraw[n], tcImage, x0, y0, pal_GetTeamColour(tc));
}
}
if (grey)
{
// disabled, render something over it!
iV_TransBoxFill(x0, y0, x0 + width(), y0 + height());
}
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// common widgets
static bool addMultiEditBox(UDWORD formid, UDWORD id, UDWORD x, UDWORD y, char const *tip, char const *tipres, UDWORD icon, UDWORD iconhi, UDWORD iconid)
{
W_EDBINIT sEdInit; // editbox
sEdInit.formID = formid;
sEdInit.id = id;
sEdInit.x = (short)x;
sEdInit.y = (short)y;
sEdInit.width = MULTIOP_EDITBOXW;
sEdInit.height = MULTIOP_EDITBOXH;
sEdInit.pText = tipres;
sEdInit.pBoxDisplay = displayMultiEditBox;
if (!widgAddEditBox(psWScreen, &sEdInit))
{
return false;
}
addMultiBut(psWScreen, MULTIOP_OPTIONS, iconid, x + MULTIOP_EDITBOXW + 2, y + 2, MULTIOP_EDITBOXH, MULTIOP_EDITBOXH, tip, icon, iconhi, iconhi);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
bool addMultiBut(W_SCREEN *screen, UDWORD formid, UDWORD id, UDWORD x, UDWORD y, UDWORD width, UDWORD height, const char* tipres, UDWORD norm, UDWORD down, UDWORD hi, unsigned tc)
{
WzMultiButton *button = new WzMultiButton(widgGetFromID(screen, formid));
button->id = id;
button->setGeometry(x, y, width, height);
button->setTip(QString::fromUtf8(tipres));
button->imNormal = Image(FrontImages, norm);
button->imDown = Image(FrontImages, down);
button->doHighlight = hi;
button->tc = tc;
return true;
}
/* Returns true if all human players clicked on the 'ready' button */
bool multiplayPlayersReady(bool bNotifyStatus)
{
unsigned int player,playerID;
bool bReady;
bReady = true;
for(player = 0; player < game.maxPlayers; player++)
{
// check if this human player is ready, ignore AIs
if (NetPlay.players[player].allocated && (!NetPlay.players[player].ready || ingame.PingTimes[player] >= PING_LIMIT))
{
if(bNotifyStatus)
{
for (playerID = 0; playerID <= game.maxPlayers && playerID != player; playerID++) ;
console("%s is not ready", getPlayerName(playerID));
}
bReady = false;
}
}
return bReady;
}