/** * @brief Buy items. * @param[in] base Pointer to the base where items are bought. * @param[in] item Pointer to the item to buy. * @param[in] number Number of items to buy. */ qboolean BS_CheckAndDoBuyItem (base_t* base, const objDef_t *item, int number) { int numItems; const int price = BS_GetItemBuyingPrice(item); const market_t *market = BS_GetMarket(); assert(base); /* you can't buy more items than there are on market */ numItems = min(number, market->numItems[item->idx]); /* you can't buy more items than you have credits for */ /** @todo Handle items with price 0 better */ if (price) numItems = min(numItems, ccs.credits / price); if (numItems <= 0) return qfalse; /* you can't buy more items than you have room for */ /** @todo Handle items with size 0 better */ if (item->size) numItems = min(numItems, (base->capacities[CAP_ITEMS].max - base->capacities[CAP_ITEMS].cur) / item->size); /* make sure that numItems is > 0 (can be negative because capacities.cur may be greater than * capacities.max if storage is disabled or if alien items have been collected on mission */ if (numItems <= 0) { UI_Popup(_("Not enough storage space"), _("You cannot buy this item.\nNot enough space in storage.\nBuild more storage facilities.")); return qfalse; } B_UpdateStorageAndCapacity(base, item, numItems, qfalse, qfalse); BS_RemoveItemFromMarket(item, numItems); CL_UpdateCredits(ccs.credits - price * numItems); return qtrue; }
/** * @brief Hires the employee in a base. * @param[in] base Which base the employee should be hired in * @param[in] employee Which employee to hire * @sa E_HireEmployeeByType * @sa E_UnhireEmployee * @todo handle EMPL_ROBOT capacities here? */ qboolean E_HireEmployee (base_t* base, employee_t* employee) { if (base->capacities[CAP_EMPLOYEES].cur >= base->capacities[CAP_EMPLOYEES].max) { UI_Popup(_("Not enough quarters"), _("You don't have enough quarters for your employees.\nBuild more quarters.")); return qfalse; } if (employee) { /* Now uses quarter space. */ employee->baseHired = base; /* Update other capacities */ switch (employee->type) { case EMPL_WORKER: base->capacities[CAP_EMPLOYEES].cur++; PR_UpdateProductionCap(base); break; case EMPL_PILOT: AIR_AutoAddPilotToAircraft(base, employee); case EMPL_SCIENTIST: case EMPL_SOLDIER: base->capacities[CAP_EMPLOYEES].cur++; break; case EMPL_ROBOT: base->capacities[CAP_ITEMS].cur += UGV_SIZE; break; case MAX_EMPL: break; } return qtrue; } return qfalse; }
/** * @brief Move item between containers. * @param[in] inv Pointer to the list where the item is currently in. * @param[in] toContainer Pointer to target container, to place the item in. * @param[in] px target x position in the toContainer container. * @param[in] py target y position in the toContainer container. * @param[in] fromContainer Pointer to source container, the item is in. * @param[in] fItem Pointer to item being moved. * @note If you set px or py to -1/NONE the item is automatically placed on * @note a free spot in the targetContainer * @return true if the move was successful. */ bool INV_MoveItem (inventory_t* inv, const invDef_t * toContainer, int px, int py, const invDef_t * fromContainer, invList_t *fItem, invList_t **tItem) { int moved; if (px >= SHAPE_BIG_MAX_WIDTH || py >= SHAPE_BIG_MAX_HEIGHT) return false; if (!fItem) return false; if (!INVSH_CheckAddingItemToInventory(inv, fromContainer->id, toContainer->id, fItem->item, GAME_GetChrMaxLoad(GAME_GetSelectedChr()))) { UI_Popup(_("Warning"), _("This soldier can not carry anything else.")); return false; } /* move the item */ moved = cls.i.MoveInInventory(&cls.i, inv, fromContainer, fItem, toContainer, px, py, NULL, tItem); switch (moved) { case IA_MOVE: case IA_ARMOUR: case IA_RELOAD: case IA_RELOAD_SWAP: return true; default: return false; } }
/** * @brief Console command to load a savegame * @sa SAV_GameLoad */ static void SAV_GameLoad_f (void) { const char *error = NULL; /* get argument */ if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0)); return; } /* Check if savegame exists */ if (FS_CheckFile("save/%s.%s", Cmd_Argv(1), SAVEGAME_EXTENSION) <= 0) { Com_Printf("savegame file '%s' doesn't exist or an empty file\n", Cmd_Argv(1)); return; } Com_DPrintf(DEBUG_CLIENT, "load file '%s'\n", Cmd_Argv(1)); /* load and go to map */ if (!SAV_GameLoad(Cmd_Argv(1), &error)) { Cbuf_Execute(); /* wipe outstanding campaign commands */ Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : ""); UI_Popup(_("Error"), popupText); Cmd_ExecuteString("game_exit"); } }
/** * @brief Move item between containers. * @param[in] inv Pointer to the inventory we are working on. * @param[in] toContainer Pointer to target container, to place the item in. * @param[in] toX target x position in the toContainer container. * @param[in] toY target y position in the toContainer container. * @param[in] fromContainer Pointer to source container, the item is in. * @param[in] fItem Pointer to item being moved. * @param[out] uponItem The item the moving item is eventually dropped upon. * @note If you set toX or toY to -1/NONE the item is automatically placed on * @note a free spot in the targetContainer * @return true if the move was successful. */ bool INV_MoveItem (inventory_t* inv, const invDef_t *toContainer, int toX, int toY, const invDef_t *fromContainer, invList_t *fItem, invList_t **uponItem) { int moved; if (toX >= SHAPE_BIG_MAX_WIDTH || toY >= SHAPE_BIG_MAX_HEIGHT) return false; if (!fItem) return false; if (!inv->canHoldItemWeight(fromContainer->id, toContainer->id, *fItem, GAME_GetChrMaxLoad(GAME_GetSelectedChr()))) { UI_Popup(_("Warning"), _("This soldier can not carry anything else.")); return false; } /* move the item */ moved = cls.i.moveInInventory(inv, fromContainer, fItem, toContainer, toX, toY, nullptr, uponItem); switch (moved) { case IA_MOVE: case IA_ARMOUR: case IA_RELOAD: case IA_RELOAD_SWAP: return true; default: return false; } }
/** * @brief Console command binding for save function * @sa SAV_GameSave * @note called via 'game_save' command */ static void SAV_GameSave_f (void) { char comment[MAX_VAR] = ""; char *error = NULL; qboolean result; /* get argument */ if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <filename> <comment|*cvar>\n", Cmd_Argv(0)); return; } if (!CP_IsRunning()) { Com_Printf("No running game - no saving...\n"); return; } /* get comment */ if (Cmd_Argc() > 2) { const char *arg = Cmd_Argv(2); Q_strncpyz(comment, arg, sizeof(comment)); } result = SAV_GameSave(Cmd_Argv(1), comment, &error); if (!result) { if (error) Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error saving game."), error); else Com_sprintf(popupText, sizeof(popupText), "%s\n", _("Error saving game.")); UI_Popup(_("Note"), popupText); } }
/** * @return @c true if are a compatible client and nothing else must be downloaded or no downloads are still running, * @c false if the start of the match must get a little bit postponed (running downloads). * @note throws ERR_DISCONNECT if we are not compatible to the server */ static bool CL_CanMultiplayerStart (void) { const int day = CL_GetConfigStringInteger(CS_LIGHTMAP); const char* serverVersion = CL_GetConfigString(CS_VERSION); /* checksum doesn't match with the one the server gave us via configstring */ if (!Q_streq(UFO_VERSION, serverVersion)) { Com_sprintf(popupText, sizeof(popupText), _("Local game version (%s) differs from the server version (%s)"), UFO_VERSION, serverVersion); UI_Popup(_("Error"), popupText); Com_Error(ERR_DISCONNECT, "Local game version (%s) differs from the server version (%s)", UFO_VERSION, serverVersion); /* amount of objects from script files doesn't match */ } else if (csi.numODs != CL_GetConfigStringInteger(CS_OBJECTAMOUNT)) { UI_Popup(_("Error"), _("Script files are not the same")); Com_Error(ERR_DISCONNECT, "Script files are not the same"); } /* activate the map loading screen for multiplayer, too */ SCR_BeginLoadingPlaque(); /* check download */ if (cls.downloadMaps) { /* confirm map */ if (CL_DownloadMap(CL_GetConfigString(CS_NAME))) return false; cls.downloadMaps = false; } /* map might still be downloading? */ if (CL_PendingHTTPDownloads()) return false; if (Com_GetScriptChecksum() != CL_GetConfigStringInteger(CS_UFOCHECKSUM)) Com_Printf("You are using modified ufo script files - might produce problems\n"); CM_LoadMap(CL_GetConfigString(CS_TILES), day, CL_GetConfigString(CS_POSITIONS), CL_GetConfigString(CS_ENTITYSTRING), cl.mapData, cl.mapTiles); #if 0 if (cl.mapData->mapChecksum != CL_GetConfigStringInteger(CS_MAPCHECKSUM)) { UI_Popup(_("Error"), _("Local map version differs from server")); Com_Error(ERR_DISCONNECT, "Local map version differs from server: %u != '%i'", cl.mapData->mapChecksum, CL_GetConfigStringInteger(CS_MAPCHECKSUM)); } #endif return true; }
/** * @brief After a mission was finished this function is called * @param msg The network message buffer * @param winner The winning team * @param numSpawned The amounts of all spawned actors per team * @param numAlive The amount of survivors per team * @param numKilled The amount of killed actors for all teams. The first dimension contains * the attacker team, the second the victim team * @param numStunned The amount of stunned actors for all teams. The first dimension contains * the attacker team, the second the victim team */ void GAME_SK_Results (struct dbuffer *msg, int winner, int *numSpawned, int *numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS]) { char resultText[UI_MAX_SMALLTEXTLEN]; int enemiesKilled, enemiesStunned; int i; const int team = cls.team; CL_Drop(); if (winner == 0) { UI_Popup(_("Game Drawn!"), _("The game was a draw!\n\nNo survivors left on any side.")); return; } enemiesKilled = enemiesStunned = 0; for (i = 0; i < MAX_TEAMS; i++) { if (i != team && i != TEAM_CIVILIAN) { enemiesKilled += numKilled[team][i]; enemiesStunned += numStunned[team][i]; } } Com_sprintf(resultText, sizeof(resultText), _("Enemies killed:\t\t%i\n" "Team survivors:\t\t%i\n" "Enemy survivors:\t\t%i\n" "Friendly fire:\t\t%i\n" "Civilians killed:\t\t%i\n" "Civilians killed by enemy:\t\t%i\n"), enemiesKilled + enemiesStunned, numAlive[team], numAlive[TEAM_ALIEN], numKilled[team][team], numKilled[team][TEAM_CIVILIAN], numKilled[TEAM_ALIEN][TEAM_CIVILIAN]); if (winner == team) { Com_sprintf(popupText, lengthof(popupText), "%s\n%s", _("You won the game!"), resultText); UI_Popup(_("Congratulations"), popupText); } else { Com_sprintf(popupText, lengthof(popupText), "%s\n%s", _("You've lost the game!"), resultText); UI_Popup(_("Better luck next time"), popupText); } }
/** * @brief Update the equipment weight for the selected actor. */ static void INV_UpdateActorLoad_f (void) { if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <callback>\n", Cmd_Argv(0)); return; } const character_t* chr = GAME_GetSelectedChr(); if (chr == nullptr) return; const float invWeight = chr->inv.getWeight(); const int maxWeight = GAME_GetChrMaxLoad(chr); const float penalty = GET_ENCUMBRANCE_PENALTY(invWeight, maxWeight); const int normalTU = GET_TU(chr->score.skills[ABILITY_SPEED], 1.0f - WEIGHT_NORMAL_PENALTY); const int tus = GET_TU(chr->score.skills[ABILITY_SPEED], penalty); const int tuPenalty = tus - normalTU; int count = 0; const Container* cont = nullptr; while ((cont = chr->inv.getNextCont(cont))) { if (cont->def()->temp) continue; for (Item* invList = cont->_invList, *next; invList; invList = next) { next = invList->getNext(); const fireDef_t* fireDef = invList->getFiredefs(); if (fireDef == nullptr) continue; for (int i = 0; i < MAX_FIREDEFS_PER_WEAPON; i++) { if (fireDef[i].time <= 0) continue; if (fireDef[i].time <= tus) continue; if (count <= 0) Com_sprintf(popupText, sizeof(popupText), _("This soldier no longer has enough TUs to use the following items:\n\n")); Q_strcat(popupText, sizeof(popupText), "%s: %s (%i)\n", _(invList->def()->name), _(fireDef[i].name), fireDef[i].time); ++count; } } } if ((Cmd_Argc() < 3 || atoi(Cmd_Argv(2)) == 0) && count > 0) UI_Popup(_("Warning"), popupText); char label[MAX_VAR]; char tooltip[MAX_VAR]; Com_sprintf(label, sizeof(label), "%g/%i %s %s", invWeight / WEIGHT_FACTOR, maxWeight, _("Kg"), (count > 0 ? _("Warning!") : "")); Com_sprintf(tooltip, sizeof(tooltip), "%s %i (%+i)", _("TU:"), tus, tuPenalty); UI_ExecuteConfunc("%s \"%s\" \"%s\" %f %i", Cmd_Argv(1), label, tooltip, WEIGHT_NORMAL_PENALTY - (1.0f - penalty), count); }
/** * @brief Loads the quick save slot * @sa SAV_GameQuickSave_f */ static void SAV_GameQuickLoad_f (void) { const char *error = NULL; if (cgi->CL_OnBattlescape()) { Com_Printf("Could not load the campaign while you are on the battlefield\n"); return; } if (!SAV_GameLoad("slotquick", &error)) { Cbuf_Execute(); /* wipe outstanding campaign commands */ Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : ""); UI_Popup(_("Error"), popupText); } else { MS_AddNewMessage(_("Campaign loaded"), _("Quicksave campaign was successfully loaded."), qfalse, MSG_INFO, NULL); CP_CheckBaseAttacks(); } }
/** * @brief Loads the last saved game * @note At saving the archive cvar cl_lastsave was set to the latest savegame * @sa SAV_GameLoad */ static void SAV_GameContinue_f (void) { const char *error = NULL; if (cgi->CL_OnBattlescape()) { UI_PopWindow(qfalse); return; } if (!CP_IsRunning()) { /* try to load the last saved campaign */ if (!SAV_GameLoad(cl_lastsave->string, &error)) { Cbuf_Execute(); /* wipe outstanding campaign commands */ Com_sprintf(popupText, sizeof(popupText), "%s\n%s", _("Error loading game."), error ? error : ""); UI_Popup(_("Error"), popupText); Cmd_ExecuteString("game_exit"); } } else { /* just continue the current running game */ UI_PopWindow(qfalse); } }
/** * @brief Responses to broadcasts, etc * @sa CL_ReadPackets * @sa CL_Frame * @sa SVC_DirectConnect * @param[in,out] msg The client stream message buffer to read from */ static void CL_ConnectionlessPacket (dbuffer* msg) { char s[512]; NET_ReadStringLine(msg, s, sizeof(s)); Cmd_TokenizeString(s, false); const char* c = Cmd_Argv(0); Com_DPrintf(DEBUG_CLIENT, "server OOB: %s (%s)\n", c, Cmd_Args()); /* server connection */ if (Q_streq(c, CL_CMD_CLIENT_CONNECT)) { int i; for (i = 1; i < Cmd_Argc(); i++) { if (char const* const p = Q_strstart(Cmd_Argv(i), "dlserver=")) { Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "ufo://%s", cls.servername); CL_SetHTTPServer(p); if (cls.downloadServer[0]) Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer); } } if (cls.state == ca_connected) { Com_Printf("Dup connect received. Ignored.\n"); return; } dbuffer buf(5); NET_WriteByte(&buf, clc_stringcmd); NET_WriteString(&buf, NET_STATE_NEW "\n"); NET_WriteMsg(cls.netStream, buf); GAME_InitMissionBriefing(_("Loading")); return; } /* remote command from gui front end */ if (Q_streq(c, CL_CMD_COMMAND)) { if (!NET_StreamIsLoopback(cls.netStream)) { Com_Printf("Command packet from remote host. Ignored.\n"); return; } else { char str[512]; NET_ReadString(msg, str, sizeof(str)); Cbuf_AddText("%s\n", str); } return; } /* ping from server */ if (Q_streq(c, CL_CMD_PING)) { NET_OOB_Printf(cls.netStream, SV_CMD_ACK); return; } /* echo request from server */ if (Q_streq(c, CL_CMD_ECHO)) { NET_OOB_Printf(cls.netStream, "%s", Cmd_Argv(1)); return; } /* print */ if (Q_streq(c, SV_CMD_PRINT)) { NET_ReadString(msg, popupText, sizeof(popupText)); /* special reject messages needs proper handling */ if (strstr(popupText, REJ_PASSWORD_REQUIRED_OR_INCORRECT)) { UI_PushWindow("serverpassword"); if (Q_strvalid(Cvar_GetString("password"))) { Cvar_Set("password", ""); CL_Drop(); UI_Popup(_("Connection failure"), _("The password you specified was wrong.")); } else { CL_Drop(); UI_Popup(_("Connection failure"), _("This server requires a password.")); } } else if (strstr(popupText, REJ_SERVER_FULL)) { CL_Drop(); UI_Popup(_("Connection failure"), _("This server is full.")); } else if (strstr(popupText, REJ_BANNED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("You are banned on this server.")); } else if (strstr(popupText, REJ_GAME_ALREADY_STARTED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The game has already started.")); } else if (strstr(popupText, REJ_SERVER_VERSION_MISMATCH)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The server is running a different version of the game.")); } else if (strstr(popupText, BAD_RCON_PASSWORD)) { Cvar_Set("rcon_password", ""); UI_Popup(_("Bad rcon password"), _("The rcon password you specified was wrong.")); } else if (strstr(popupText, REJ_CONNECTION_REFUSED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The server refused the connection.")); } else if (Q_strvalid(popupText)) { UI_Popup(_("Notice"), _(popupText)); } return; } if (!GAME_HandleServerCommand(c, msg)) Com_Printf("Unknown command received \"%s\"\n", c); }