/** * @brief Make sure equipment definitions used to generate teams are proper. * @note Check that the sum of all probabilities is smaller or equal to 100 for a weapon type. * @sa INVSH_EquipActor */ static bool INV_EquipmentDefSanityCheck (void) { int i, j; int sum; bool result = true; for (i = 0; i < csi.numEDs; i++) { const equipDef_t *const ed = &csi.eds[i]; /* only check definitions used for generating teams */ if (!Q_strstart(ed->id, "alien") && !Q_strstart(ed->id, "phalanx")) continue; /* Check primary */ sum = 0; for (j = 0; j < csi.numODs; j++) { const objDef_t *const obj = INVSH_GetItemByIDX(j); if (obj->weapon && obj->fireTwoHanded && (INV_ItemMatchesFilter(obj, FILTER_S_PRIMARY) || INV_ItemMatchesFilter(obj, FILTER_S_HEAVY))) sum += ed->numItems[j]; } if (sum > 100) { Com_Printf("INV_EquipmentDefSanityCheck: Equipment Def '%s' has a total probability for primary weapons greater than 100\n", ed->id); result = false; } /* Check secondary */ sum = 0; for (j = 0; j < csi.numODs; j++) { const objDef_t *const obj = INVSH_GetItemByIDX(j); if (obj->weapon && obj->reload && !obj->deplete && INV_ItemMatchesFilter(obj, FILTER_S_SECONDARY)) sum += ed->numItems[j]; } if (sum > 100) { Com_Printf("INV_EquipmentDefSanityCheck: Equipment Def '%s' has a total probability for secondary weapons greater than 100\n", ed->id); result = false; } /* Check armour */ sum = 0; for (j = 0; j < csi.numODs; j++) { const objDef_t *const obj = INVSH_GetItemByIDX(j); if (INV_ItemMatchesFilter(obj, FILTER_S_ARMOUR)) sum += ed->numItems[j]; } if (sum > 100) { Com_Printf("INV_EquipmentDefSanityCheck: Equipment Def '%s' has a total probability for armours greater than 100\n", ed->id); result = false; } /* Don't check misc: the total probability can be greater than 100 */ } return result; }
/** * @brief Remove items until everything fits in storage. * @note items will be randomly selected for removal. * @param[in] base Pointer to the base */ void CAP_RemoveItemsExceedingCapacity (base_t *base) { int i; int objIdx[MAX_OBJDEFS]; /**< Will contain idx of items that can be removed */ int num, cnt; if (CAP_GetFreeCapacity(base, CAP_ITEMS) >= 0) return; for (i = 0, num = 0; i < cgi->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (!B_ItemIsStoredInBaseStorage(obj)) continue; /* Don't count item that we don't have in base */ if (B_ItemInBase(obj, base) <= 0) continue; objIdx[num++] = i; } cnt = E_CountHired(base, EMPL_ROBOT); /* UGV takes room in storage capacity: we store them with a value MAX_OBJDEFS that can't be used by objIdx */ for (i = 0; i < cnt; i++) { objIdx[num++] = MAX_OBJDEFS; } while (num && CAP_GetFreeCapacity(base, CAP_ITEMS) < 0) { /* Select the item to remove */ const int randNumber = rand() % num; if (objIdx[randNumber] >= MAX_OBJDEFS) { /* A UGV is destroyed: get first one */ Employee* employee = E_GetHiredRobot(base, 0); /* There should be at least a UGV */ assert(employee); E_DeleteEmployee(employee); } else { /* items are destroyed. We guess that all items of a given type are stored in the same location * => destroy all items of this type */ const int idx = objIdx[randNumber]; const objDef_t *od = INVSH_GetItemByIDX(idx); B_UpdateStorageAndCapacity(base, od, -B_ItemInBase(od, base), false); } REMOVE_ELEM(objIdx, randNumber, num); /* Make sure that we don't have an infinite loop */ if (num <= 0) break; } Com_DPrintf(DEBUG_CLIENT, "B_RemoveItemsExceedingCapacity: Remains %i in storage for a maximum of %i\n", CAP_GetCurrent(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS)); }
/** * @note Called at precache phase - only load these soundfiles once at startup or on sound restart * @sa S_Restart_f */ void S_PrecacheSamples (void) { int i, j, k; if (!s_env.initialized) return; /* load weapon sounds */ for (i = 0; i < csi.numODs; i++) { /* i = obj */ const objDef_t *od = INVSH_GetItemByIDX(i); for (j = 0; j < od->numWeapons; j++) { /* j = weapon-entry per obj */ for (k = 0; k < od->numFiredefs[j]; k++) { /* k = firedef per weapon */ const fireDef_t *fd = &od->fd[j][k]; if (fd->fireSound[0] != '\0') S_LoadSample(fd->fireSound); if (fd->impactSound[0] != '\0') S_LoadSample(fd->impactSound); if (fd->hitBodySound[0] != '\0') S_LoadSample(fd->hitBodySound); if (fd->bounceSound[0] != '\0') S_LoadSample(fd->bounceSound); } } } /* precache the sound pool */ stdSoundPool[SOUND_WATER_IN] = S_LoadSample("footsteps/water_in"); stdSoundPool[SOUND_WATER_OUT] = S_LoadSample("footsteps/water_out"); stdSoundPool[SOUND_WATER_MOVE] = S_LoadSample("footsteps/water_under"); }
void CL_InvAmmo (const eventRegister_t* self, dbuffer* msg) { int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le_t* le = LE_Get(number); if (!le) { Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n"); return; } if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); Item* item = le->inv.getItemAtPos(INVDEF(container), x, y); if (!item) return; /* set new ammo */ item->setAmmoLeft(ammo); item->setAmmoDef(INVSH_GetItemByIDX(type)); }
/** * @brief Throw item with actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa EV_ACTOR_THROW */ void CL_ActorDoThrow (const eventRegister_t *self, dbuffer *msg) { vec3_t muzzle, v0; int flags; int dtime; int objIdx; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; /* read data */ NET_ReadFormat(msg, self->formatString, &dtime, &objIdx, &weapFdsIdx, &fdIdx, &flags, &muzzle, &v0); /* get the fire def */ const objDef_t *obj = INVSH_GetItemByIDX(objIdx); const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); /* add effect le (local entity) */ /** @todo add victim support for blood and hurt sounds */ LE_AddGrenade(fd, flags, muzzle, v0, dtime, nullptr); /* start the sound */ if (fd->fireSound != nullptr && !(flags & SF_BOUNCED)) { S_LoadAndPlaySample(fd->fireSound, muzzle, SOUND_ATTN_IDLE, SND_VOLUME_DEFAULT); } }
void CL_InvAmmo (const eventRegister_t *self, dbuffer *msg) { invList_t *ic; le_t *le; int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le = LE_Get(number); if (!le) { Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n"); return; } if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y); if (!ic) return; /* set new ammo */ ic->item.ammoLeft = ammo; ic->item.ammo = INVSH_GetItemByIDX(type); }
/** * @brief This is called after the actors are spawned and will set actor states without consuming TUs * @param player The player to perform the action for */ void G_ClientInitActorStates (const player_t * player) { const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */ int i; for (i = 0; i < length; i++) { const int ucn = gi.ReadShort(); int saveTU; actorHands_t hand; int fmIdx, objIdx; edict_t *ent = G_EdictsGetActorByUCN(ucn, player->pers.team); if (!ent) gi.Error("Could not find character on team %i with unique character number %i", player->pers.team, ucn); /* these state changes are not consuming any TUs */ saveTU = ent->TU; G_ClientStateChange(player, ent, gi.ReadShort(), false); hand = (actorHands_t)gi.ReadShort(); fmIdx = gi.ReadShort(); objIdx = gi.ReadShort(); G_ActorSetTU(ent, saveTU); if (objIdx != NONE) { G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx)); } G_ClientStateChangeUpdate(ent); } }
/** * @brief Save callback for savegames * @param[out] parent XML Node structure, where we write the information to * @sa BS_LoadXML * @sa SAV_GameSaveXML */ bool BS_SaveXML (xmlNode_t* parent) { int i; xmlNode_t* node; const market_t* market = BS_GetMarket(); /* store market */ node = cgi->XML_AddNode(parent, SAVE_MARKET_MARKET); for (i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); if (BS_IsOnMarket(od)) { xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_MARKET_ITEM); cgi->XML_AddString(snode, SAVE_MARKET_ID, od->id); cgi->XML_AddIntValue(snode, SAVE_MARKET_NUM, market->numItems[i]); cgi->XML_AddIntValue(snode, SAVE_MARKET_BID, market->bidItems[i]); cgi->XML_AddIntValue(snode, SAVE_MARKET_ASK, market->askItems[i]); cgi->XML_AddDoubleValue(snode, SAVE_MARKET_EVO, market->currentEvolutionItems[i]); cgi->XML_AddBoolValue(snode, SAVE_MARKET_AUTOSELL, market->autosell[i]); } } for (i = 0; i < AIRCRAFTTYPE_MAX; i++) { if (market->bidAircraft[i] > 0 || market->askAircraft[i] > 0) { xmlNode_t* snode = cgi->XML_AddNode(node, SAVE_MARKET_AIRCRAFT); const char* shortName = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i); cgi->XML_AddString(snode, SAVE_MARKET_ID, shortName); cgi->XML_AddIntValue(snode, SAVE_MARKET_NUM, market->numAircraft[i]); cgi->XML_AddIntValue(snode, SAVE_MARKET_BID, market->bidAircraft[i]); cgi->XML_AddIntValue(snode, SAVE_MARKET_ASK, market->askAircraft[i]); cgi->XML_AddDoubleValue(snode, SAVE_MARKET_EVO, market->currentEvolutionAircraft[i]); } } return true; }
/** * @brief Decides if following events should be delayed */ int CL_ActorShootHiddenTime (const eventRegister_t *self, dbuffer *msg, eventTiming_t *eventTiming) { int first; int objIdx; const objDef_t *obj; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fireDefIndex; const int eventTime = eventTiming->shootTime; NET_ReadFormat(msg, self->formatString, &first, &objIdx, &weapFdsIdx, &fireDefIndex); obj = INVSH_GetItemByIDX(objIdx); if (first) { eventTiming->nextTime += 500; eventTiming->impactTime = eventTiming->shootTime = eventTiming->nextTime; } else { const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fireDefIndex); /* impact right away - we don't see it at all * bouncing is not needed here, too (we still don't see it) */ eventTiming->impactTime = eventTiming->shootTime; eventTiming->nextTime = CL_GetNextTime(self, eventTiming, eventTiming->shootTime + 1400); if (fd->delayBetweenShots > 0.0) eventTiming->shootTime += 1000 / fd->delayBetweenShots; } eventTiming->parsedDeath = false; return eventTime; }
/** * @brief Precaches all models at game startup - for faster access */ void CL_ViewPrecacheModels (void) { float percent = 30.0f; float alreadyLoadedPercent = 30.0f; float loaded = CL_PrecacheCharacterModels(alreadyLoadedPercent); alreadyLoadedPercent += loaded; if (loaded == 0) percent = 100 - alreadyLoadedPercent; for (int i = 0; i < csi.numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); alreadyLoadedPercent += percent / csi.numODs; SCR_DrawLoadingScreen(true, alreadyLoadedPercent); if (od->type[0] == '\0' || od->isDummy) continue; if (od->model[0] != '\0') { cls.modelPool[i] = R_FindModel(od->model); if (cls.modelPool[i]) Com_DPrintf(DEBUG_CLIENT, "CL_PrecacheModels: Registered object model: '%s' (%i)\n", od->model, i); } } /* now make sure that all the precached models are stored until we quit the game * otherwise they would be freed with every map change */ R_SwitchModelMemPoolTag(); SCR_DrawLoadingScreen(false, 100.f); }
/** * @brief Make sure values of items after parsing are proper. */ static qboolean CP_ItemsSanityCheck (void) { int i; qboolean result = qtrue; for (i = 0; i < csi.numODs; i++) { const objDef_t *item = INVSH_GetItemByIDX(i); /* Warn if item has no size set. */ if (item->size <= 0 && B_ItemIsStoredInBaseStorage(item)) { result = qfalse; Com_Printf("CP_ItemsSanityCheck: Item %s has zero size set.\n", item->id); } /* Warn if no price is set. */ if (item->price <= 0 && BS_IsOnMarket(item)) { result = qfalse; Com_Printf("CP_ItemsSanityCheck: Item %s has zero price set.\n", item->id); } if (item->price > 0 && !BS_IsOnMarket(item) && !PR_ItemIsProduceable(item)) { result = qfalse; Com_Printf("CP_ItemsSanityCheck: Item %s has a price set though it is neither available on the market and production.\n", item->id); } /* extension and headgear are mutual exclusive */ if (item->extension && item->headgear) { result = qfalse; Com_Printf("CP_ItemsSanityCheck: Item %s has both extension and headgear set.\n", item->id); } } return result; }
/** * @brief Draw an item node */ void uiItemNode::draw (uiNode_t *node) { const objDef_t *od; const char* ref = UI_GetReferenceString(node, EXTRADATA(node).model); vec2_t pos; if (Q_strnull(ref)) return; UI_GetNodeAbsPos(node, pos); R_CleanupDepthBuffer(pos[0], pos[1], node->box.size[0], node->box.size[1]); od = INVSH_GetItemByIDSilent(ref); if (od) { item_t item = {1, NULL, NULL, 0, 0}; /* 1 so it's not reddish; fake item anyway */ item.t = INVSH_GetItemByIDX(od->idx); if (EXTRADATA(node).containerLike || INV_IsArmour(item.t)) { const vec4_t color = {1, 1, 1, 1}; vec3_t itemNodePos; /* We position the model of the item ourself (in the middle of the item * node). See the "-1, -1" parameter of UI_DrawItem. */ UI_GetNodeAbsPos(node, itemNodePos); itemNodePos[0] += node->box.size[0] / 2.0; itemNodePos[1] += node->box.size[1] / 2.0; itemNodePos[2] = 0; /** @todo we should not use DrawItem but draw the image with render function (remove dependency with container) */ UI_DrawItem(node, itemNodePos, &item, -1, -1, EXTRADATA(node).scale, color); } else { UI_DrawModelNode(node, GAME_GetModelForItem(item.t, NULL)); } } else { GAME_DisplayItemInfo(node, ref); } }
/** * @brief Fills the ground container of the ui_inventory with unused items from a given * equipment definition * @note Keep in mind that @c ed is changed here - so items are removed and the ground container * of a inventory definition is in general a temp container - that means you should make a copy * of the @c equipDef_t you want to add to the temp ground container of the given @c inv * @todo it's not obvious for the caller that @c ui_inventory pointer must be set * @param[in,out] inv The inventory to add the unused items from @c ed to * @param[in,out] ed The equipment definition to get the used items from that should be added * to the ground container of @c inv * @todo not used nor called by the container node; should be move somewhere else */ void UI_ContainerNodeUpdateEquipment (inventory_t *inv, const equipDef_t *ed) { int i; int *const numItems = Mem_Dup(int, ed->numItems, lengthof(ed->numItems)); /* a 'tiny hack' to add the remaining equipment (not carried) * correctly into buy categories, reloading at the same time; * it is valid only due to the following property: */ assert(MAX_CONTAINERS >= FILTER_AIRCRAFT); for (i = 0; i < csi.numODs; i++) { const objDef_t *od = INVSH_GetItemByIDX(i); /* Don't allow to show unuseable items. */ if (!GAME_ItemIsUseable(od)) continue; while (numItems[i]) { const item_t item = {NONE_AMMO, NULL, od, 0, 0}; if (!cls.i.AddToInventory(&cls.i, inv, &item, INVDEF(csi.idEquip), NONE, NONE, 1)) { /* no space left in the inventory */ break; } numItems[item.item->idx]--; } } Mem_Free(numItems); /* First-time linking of ui_inventory. */ if (ui_inventory && !ui_inventory->c[csi.idEquip]) { ui_inventory->c[csi.idEquip] = inv->c[csi.idEquip]; } }
/** * @sa G_WriteItem * @sa G_ReadItem * @note The amount of the item_t struct should not be needed here - because * the amount is only valid for idFloor and idEquip */ static void CL_NetReceiveItem (dbuffer *buf, item_t *item, containerIndex_t *container, int *x, int *y) { const eventRegister_t *eventData = CL_GetEvent(EV_INV_TRANSFER); /* reset */ int t, m; item->item = item->ammo = NULL; item->ammoLeft = NONE_AMMO; NET_ReadFormat(buf, eventData->formatString, &t, &item->ammoLeft, &m, container, x, y, &item->rotated, &item->amount); item->item = INVSH_GetItemByIDX(t); item->ammo = INVSH_GetItemByIDX(m); if (!item->item) Com_Error(ERR_DROP, "no weapon given for item"); }
/** * @brief Update the GUI with the selected item */ static void INV_UpdateObject_f (void) { /* check syntax */ if (Cmd_Argc() < 3) { Com_Printf("Usage: %s <objectid> <confunc> [mustwechangetab]\n", Cmd_Argv(0)); return; } bool changeTab = true; if (Cmd_Argc() == 4) changeTab = atoi(Cmd_Argv(3)) >= 1; const int num = atoi(Cmd_Argv(1)); if (num < 0 || num >= csi.numODs) { Com_Printf("Id %i out of range 0..%i\n", num, csi.numODs); return; } const objDef_t* obj = INVSH_GetItemByIDX(num); /* update tab */ if (changeTab) { const cvar_t* var = Cvar_FindVar("mn_equiptype"); const int filter = INV_GetFilterFromItem(obj); if (var && var->integer != filter) { Cvar_SetValue("mn_equiptype", filter); UI_ExecuteConfunc("%s", Cmd_Argv(2)); } } /* update item description */ INV_ItemDescription(obj); }
/** * @brief Get the correct animation for the given actor state and weapons * @param[in] anim Type of animation (for example "stand", "walk") * @param[in] right ods index to determine the weapon in the actors right hand * @param[in] left ods index to determine the weapon in the actors left hand * @param[in] state the actors state - e.g. STATE_CROUCHED (crouched animations) * have a 'c' in front of their animation definitions (see *.anm files for * characters) */ const char* LE_GetAnim (const char* anim, int right, int left, int state) { if (!anim) return ""; static char retAnim[MAX_VAR]; char* mod = retAnim; size_t length = sizeof(retAnim); /* add crouched flag */ if (state & STATE_CROUCHED) { *mod++ = 'c'; length--; } /* determine relevant data */ char animationIndex; char const* type; if (right == NONE) { animationIndex = '0'; if (left == NONE) type = "item"; else { type = INVSH_GetItemByIDX(left)->type; /* left hand grenades look OK with default anim; others don't */ if (!Q_streq(type, "grenade")) { type = "pistol_d"; } } } else { const objDef_t* od = INVSH_GetItemByIDX(right); animationIndex = od->animationIndex; type = od->type; if (left != NONE && Q_streq(od->type, "pistol") && Q_streq(INVSH_GetItemByIDX(left)->type, "pistol")) { type = "pistol_d"; } } if (Q_strstart(anim, "stand") || Q_strstart(anim, "walk")) { Com_sprintf(mod, length, "%s%c", anim, animationIndex); } else { Com_sprintf(mod, length, "%s_%s", anim, type); } return retAnim; }
/** * @sa G_WriteItem * @sa G_ReadItem * @note The amount of the Item should not be needed here - because * the amount is only valid for CID_FLOOR and CID_EQUIP */ static void CL_NetReceiveItem (dbuffer* buf, Item* item, containerIndex_t* container, int* x, int* y) { const eventRegister_t* eventData = CL_GetEvent(EV_INV_TRANSFER); /* reset */ int itemIdx, ammoIdx; int ammoleft = NONE_AMMO; int amount = 0; item->setDef(nullptr); item->setAmmoDef(nullptr); NET_ReadFormat(buf, eventData->formatString, &itemIdx, &ammoleft, &ammoIdx, container, x, y, &item->rotated, &amount); item->setAmmoLeft(ammoleft); item->setAmount(amount); item->setDef(INVSH_GetItemByIDX(itemIdx)); item->setAmmoDef(INVSH_GetItemByIDX(ammoIdx)); if (!item->def()) Com_Error(ERR_DROP, "no weapon given for item"); }
static void testItemMassActions (void) { ResetInventoryList(); const objDef_t* od = INVSH_GetItemByIDSilent("assault"); CU_ASSERT_PTR_NOT_NULL_FATAL(od); const invDef_t* container = INVSH_GetInventoryDefinitionByID("right"); CU_ASSERT_PTR_NOT_NULL_FATAL(container); Inventory inv; bool addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == true); /* second try should fail as the right container is a single container */ addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == false); container = INVSH_GetInventoryDefinitionByID("left"); CU_ASSERT_PTR_NOT_NULL_FATAL(container); od = INVSH_GetItemByIDSilent("fraggrenade"); CU_ASSERT_PTR_NOT_NULL_FATAL(od); addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == true); container = INVSH_GetInventoryDefinitionByID("equip"); CU_ASSERT_PTR_NOT_NULL_FATAL(container); for (int i = 0; i < csi.numODs; i++) { od = INVSH_GetItemByIDX(i); /* every item should be placable on the ground container and there should really be enough space */ addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od, container); CU_ASSERT(addedItem == true); for (int j = 0; j < od->numAmmos; j++) { addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); addedItem = testAddSingle(&inv, od->ammos[j], container); CU_ASSERT(addedItem == true); } } }
static void testTeamDefsModelScriptData (void) { int i; linkedList_t *armourPaths = NULL; for (i = 0; i < csi.numTeamDefs; i++) { int j; const teamDef_t *teamDef = &csi.teamDef[i]; if (!teamDef->armour) continue; for (j = 0; j < csi.numODs; j++) { const objDef_t *od = INVSH_GetItemByIDX(j); if (!INV_IsArmour(od)) continue; /* not for this team */ if (!CHRSH_IsArmourUseableForTeam(od, teamDef)) continue; if (!LIST_ContainsString(armourPaths, od->armourPath)) LIST_AddString(&armourPaths, od->armourPath); } UFO_CU_ASSERT_TRUE_MSG(!LIST_IsEmpty(armourPaths), va("no armour definitions found for team %s - but armour is set to true", teamDef->id)); LIST_Foreach(armourPaths, char const, armourPath) { int l; for (l = NAME_NEUTRAL; l < NAME_LAST; l++) { /* no models for this gender */ if (!teamDef->numModels[l]) continue; CU_ASSERT_PTR_NOT_NULL_FATAL(teamDef->models[l]); for (linkedList_t const* list = teamDef->models[l]; list; list = list->next) { teamDef_t::model_t const& m = *static_cast<teamDef_t::model_t const*>(list->data); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.body)), va("%s does not exist in models/%s (teamDef: %s)", m.body, m.path, teamDef->id)); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.body)), va("%s does not exist in models/%s%s (teamDef: %s)", m.body, m.path, armourPath, teamDef->id)); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.head)), va("%s does not exist in models/%s (teamDef: %s)", m.head, m.path, teamDef->id)); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.head)), va("%s does not exist in models/%s%s (teamDef: %s)", m.head, m.path, armourPath, teamDef->id)); } } } LIST_Delete(&armourPaths); }
/** * @brief Compute the next itemID * @note If something found, item type can be find with iterator->itemID * @note If item is available into the container iterator->itemFound point to this element * @note If nothing found (no next element) then iterator->itemID >= csi.numODs */ static void UI_ContainerItemIteratorNext (containerItemIterator_t* iterator) { assert(iterator->groupSteps[iterator->groupID] != CII_END); /* iterate each groups */ for (; iterator->groupSteps[iterator->groupID] != CII_END; iterator->groupID++) { int filter = iterator->groupSteps[iterator->groupID]; /* next */ iterator->itemID++; /* iterate all item type*/ for (;iterator->itemID < csi.numODs; iterator->itemID++) { const objDef_t* obj = INVSH_GetItemByIDX(iterator->itemID); /* gameplay filter */ if (!GAME_ItemIsUseable(obj)) continue; /* type filter */ /** @todo not sure its the right check */ const bool isArmour = obj->isArmour(); const bool isAmmo = obj->numWeapons != 0 && obj->isAmmo(); const bool isWeapon = obj->weapon || obj->isMisc || isArmour; const bool isImplant = obj->implant; if ((filter & CII_WEAPONONLY) && !isWeapon) continue; if ((filter & CII_AMMOONLY) && !isAmmo) continue; if ((filter & CII_IMPLANTONLY) && !isImplant) continue; if (!INV_ItemMatchesFilter(obj, iterator->filterEquipType)) continue; /* exists in inventory filter */ iterator->itemFound = UI_ContainerNodeGetExistingItem(iterator->node, obj, iterator->filterEquipType); if ((filter & CII_AVAILABLEONLY) && iterator->itemFound == nullptr) continue; if ((filter & CII_NOTAVAILABLEONLY) && iterator->itemFound != nullptr) continue; /* we found something */ return; } /* can we search into another group? */ if (iterator->groupSteps[iterator->groupID + 1] != CII_END) iterator->itemID = -1; } /* clean up */ iterator->itemFound = nullptr; }
/** * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base. * @param[in,out] destination The destination base - might be NULL in case the base * is already destroyed * @param[in] transfer Pointer to transfer in ccs.transfers. * @param[in] success True if the transfer reaches dest base, false if the base got destroyed. * @sa TR_TransferEnd */ static void TR_EmptyTransferCargo (base_t *destination, transfer_t *transfer, qboolean success) { assert(transfer); if (transfer->hasItems && success) { /* Items. */ const objDef_t *od = INVSH_GetItemByID(ANTIMATTER_TECH_ID); int i; /* antimatter */ if (transfer->itemAmount[od->idx] > 0) { if (B_GetBuildingStatus(destination, B_ANTIMATTER)) { B_ManageAntimatter(destination, transfer->itemAmount[od->idx], qtrue); } else { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); } } /* items */ for (i = 0; i < csi.numODs; i++) { od = INVSH_GetItemByIDX(i); if (transfer->itemAmount[od->idx] <= 0) continue; if (!B_ItemIsStoredInBaseStorage(od)) continue; if (!B_GetBuildingStatus(destination, B_STORAGE)) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Storage, items are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); break; } B_UpdateStorageAndCapacity(destination, od, transfer->itemAmount[od->idx], qfalse, qtrue); } } if (transfer->hasEmployees && transfer->srcBase) { /* Employees. (cannot come from a mission) */ if (!success || !B_GetBuildingStatus(destination, B_QUARTERS)) { /* Employees will be unhired. */ employeeType_t i; if (success) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Living Quarters, employees got unhired!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, qfalse, MSG_TRANSFERFINISHED, NULL); } for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) { employee_t *employee; TR_ForeachEmployee(employee, transfer, i) { employee->baseHired = transfer->srcBase; /* Restore back the original baseid. */ employee->transfer = qfalse; E_UnhireEmployee(employee); } } } else {
/** * @brief Update Storage Capacity. * @param[in] base Pointer to the base * @sa B_ResetAllStatusAndCapacities_f */ void CAP_UpdateStorageCap (base_t* base) { CAP_SetCurrent(base, CAP_ITEMS, 0); for (int i = 0; i < cgi->csi->numODs; i++) { const objDef_t* obj = INVSH_GetItemByIDX(i); if (!B_ItemIsStoredInBaseStorage(obj)) continue; CAP_AddCurrent(base, CAP_ITEMS, B_ItemInBase(obj, base) * obj->size); } /* UGV takes room in storage capacity */ CAP_AddCurrent(base, CAP_ITEMS, UGV_SIZE * E_CountHired(base, EMPL_ROBOT)); }
/** * @brief Shoot with weapon but don't bother with animations - actor is hidden. * @sa CL_ActorShoot */ void CL_ActorShootHidden (const eventRegister_t* self, dbuffer* msg) { int first; int objIdx; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; NET_ReadFormat(msg, self->formatString, &first, &objIdx, &weapFdsIdx, &fdIdx); /* get the fire def */ const objDef_t* obj = INVSH_GetItemByIDX(objIdx); const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); /* start the sound */ if ((first || !fd->soundOnce) && fd->fireSound != nullptr) S_StartLocalSample(fd->fireSound, SND_VOLUME_WEAPONS); }
/** * @brief sets market prices at start of the game * @sa CP_CampaignInit * @sa B_SetUpFirstBase * @sa BS_Load (Market load function) */ void BS_InitMarket (const campaign_t* campaign) { int i; market_t* market = BS_GetMarket(); for (i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); if (market->askItems[i] == 0) { market->askItems[i] = od->price; market->bidItems[i] = floor(market->askItems[i] * BID_FACTOR); } if (campaign->marketDef->numItems[i] <= 0) continue; if (RS_IsResearched_ptr(RS_GetTechForItem(od))) { /* the other relevant values were already set above */ market->numItems[i] = campaign->marketDef->numItems[i]; } else { Com_Printf("BS_InitMarket: Could not add item %s to the market - not marked as researched in campaign %s\n", od->id, campaign->id); } } for (i = 0; i < AIRCRAFTTYPE_MAX; i++) { const char* name = cgi->Com_DropShipTypeToShortName((humanAircraftType_t)i); const aircraft_t* aircraft = AIR_GetAircraft(name); if (market->askAircraft[i] == 0) { market->askAircraft[i] = aircraft->price; market->bidAircraft[i] = floor(market->askAircraft[i] * BID_FACTOR); } if (campaign->marketDef->numAircraft[i] <= 0) continue; if (RS_IsResearched_ptr(aircraft->tech)) { /* the other relevant values were already set above */ market->numAircraft[i] = campaign->marketDef->numAircraft[i]; } else { Com_Printf("BS_InitMarket: Could not add aircraft %s to the market - not marked as researched in campaign %s\n", aircraft->id, campaign->id); } } }
/** * @brief Network event function for reaction fire mode changes. Responsible for updating * the HUD with the information that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from * @sa HUD_UpdateReactionFiremodes */ void CL_ActorReactionFireChange (const eventRegister_t* self, dbuffer* msg) { actorHands_t hand; int entnum, fmIdx, odIdx; NET_ReadFormat(msg, self->formatString, &entnum, &fmIdx, &hand, &odIdx); const le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); character_t* chr = CL_ActorGetChr(le); if (!chr) return; const objDef_t* od = INVSH_GetItemByIDX(odIdx); chr->RFmode.set(hand, fmIdx, od); UI_ExecuteConfunc("reactionfire_updated"); }
/** * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base. * @param[in,out] destination The destination base - might be nullptr in case the base * is already destroyed * @param[in] transfer Pointer to transfer in ccs.transfers. * @param[in] success True if the transfer reaches dest base, false if the base got destroyed. * @sa TR_TransferEnd */ static void TR_EmptyTransferCargo (base_t* destination, transfer_t* transfer, bool success) { assert(transfer); if (transfer->hasItems && success) { /* Items. */ const objDef_t* od = INVSH_GetItemByID(ANTIMATTER_ITEM_ID); int i; /* antimatter */ if (transfer->itemAmount[od->idx] > 0) { if (B_GetBuildingStatus(destination, B_ANTIMATTER)) { B_ManageAntimatter(destination, transfer->itemAmount[od->idx], true); } else { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, MSG_TRANSFERFINISHED); } } /* items */ for (i = 0; i < cgi->csi->numODs; i++) { od = INVSH_GetItemByIDX(i); if (transfer->itemAmount[od->idx] <= 0) continue; if (!B_ItemIsStoredInBaseStorage(od)) continue; B_AddToStorage(destination, od, transfer->itemAmount[od->idx]); } } if (transfer->hasEmployees && transfer->srcBase) { /* Employees. (cannot come from a mission) */ if (!success) { /* Employees will be unhired. */ for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) { const employeeType_t type = (employeeType_t)i; TR_ForeachEmployee(employee, transfer, type) { employee->baseHired = transfer->srcBase; /* Restore back the original baseid. */ employee->transfer = false; employee->unhire(); } } } else { for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
/** * @brief Decides if following events should be delayed. If the projectile has a speed value assigned, the * delay is relative to the distance the projectile flies. There are other fire definition related options * that might delay the execution of further events. */ int CL_ActorDoShootTime (const eventRegister_t* self, dbuffer* msg, eventTiming_t* eventTiming) { int flags, dummy; int objIdx, surfaceFlags; int weap_fds_idx, fd_idx; shoot_types_t shootType; vec3_t muzzle, impact; int eventTime = eventTiming->shootTime; /* read data */ NET_ReadFormat(msg, self->formatString, &dummy, &dummy, &dummy, &objIdx, &weap_fds_idx, &fd_idx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &dummy); const objDef_t* obj = INVSH_GetItemByIDX(objIdx); const fireDef_t* fd = FIRESH_GetFiredef(obj, weap_fds_idx, fd_idx); if (!(flags & SF_BOUNCED)) { /* shooting */ if (fd->speed > 0.0 && !CL_OutsideMap(impact, UNIT_SIZE * 10)) { eventTiming->impactTime = eventTiming->shootTime + 1000 * VectorDist(muzzle, impact) / fd->speed; } else { eventTiming->impactTime = eventTiming->shootTime; } if (!cls.isOurRound()) eventTiming->nextTime = CL_GetNextTime(self, eventTiming, eventTiming->impactTime + 1400); else eventTiming->nextTime = CL_GetNextTime(self, eventTiming, eventTiming->impactTime + 400); if (fd->delayBetweenShots > 0.0) eventTiming->shootTime += 1000 / fd->delayBetweenShots; } else { /* only a bounced shot */ eventTime = eventTiming->impactTime; if (fd->speed > 0.0) { eventTiming->impactTime += 1000 * VectorDist(muzzle, impact) / fd->speed; eventTiming->nextTime = CL_GetNextTime(self, eventTiming, eventTiming->impactTime); } } eventTiming->parsedDeath = false; return eventTime; }
void CL_InvReload (const eventRegister_t* self, dbuffer* msg) { int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le_t* le = LE_Get(number); if (!le) return; if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); Item* ic = le->inv.getItemAtPos(INVDEF(container), x, y); if (!ic) return; S_LoadAndPlaySample(ic->def()->reloadSound, le->origin, ic->def()->reloadAttenuation, SND_VOLUME_WEAPONS); /* if the displaced clip had any remaining bullets * store them as loose, unless the removed clip was full */ equipDef_t* ed = GAME_GetEquipmentDefinition(); if (ed && ic->getAmmoLeft() > 0 && ic->getAmmoLeft() != ic->def()->ammo) { assert(ammo == ic->def()->ammo); /* Accumulate loose ammo into clips (only accessible post-mission) */ ed->addClip(ic); } /* set new ammo */ ic->setAmmoLeft(ammo); ic->setAmmoDef(INVSH_GetItemByIDX(type)); if (le == selActor) Cmd_ExecuteString("hud_updateactorload"); }
/** * @brief Lists all object definitions. * @note called with debug_listinventory */ static void INV_InventoryList_f (void) { int i; for (i = 0; i < csi.numODs; i++) { int j; const objDef_t *od = INVSH_GetItemByIDX(i); Com_Printf("Item: %s\n", od->id); Com_Printf("... name -> %s\n", od->name); Com_Printf("... type -> %s\n", od->type); Com_Printf("... category -> %i\n", od->animationIndex); Com_Printf("... weapon -> %i\n", od->weapon); Com_Printf("... holdtwohanded -> %i\n", od->holdTwoHanded); Com_Printf("... firetwohanded -> %i\n", od->fireTwoHanded); Com_Printf("... thrown -> %i\n", od->thrown); Com_Printf("... usable for weapon (if type is ammo):\n"); for (j = 0; j < od->numWeapons; j++) { if (od->weapons[j]) Com_Printf(" ... %s\n", od->weapons[j]->name); } Com_Printf("\n"); } }
/** * @brief make number of items change every day. * @sa CP_CampaignRun * @sa daily called * @note This function makes items number on market slowly reach the asymptotic number of items defined in equipment.ufo * If an item has just been researched, it's not available on market until RESEARCH_LIMIT_DELAY days is reached. */ void CP_CampaignRunMarket (campaign_t* campaign) { int i; const float TYPICAL_TIME = 10.f; /**< Number of days to reach the asymptotic number of items */ const int RESEARCH_LIMIT_DELAY = 30; /**< Numbers of days after end of research to wait in order to have * items added on market */ market_t* market = BS_GetMarket(); assert(campaign->marketDef); assert(campaign->asymptoticMarketDef); for (i = 0; i < cgi->csi->numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); const technology_t* tech = RS_GetTechForItem(od); int asymptoticNumber; if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numItems[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) { /* if items are researched for more than RESEARCH_LIMIT_DELAY or was on the initial market, * there number tend to the value defined in equipment.ufo. * This value is the asymptotic value if it is not 0, or initial value else */ asymptoticNumber = campaign->asymptoticMarketDef->numItems[i] ? campaign->asymptoticMarketDef->numItems[i] : campaign->marketDef->numItems[i]; } else { /* items that have just been researched don't appear on market, but they can disappear */ asymptoticNumber = 0; } /* Store the evolution of the market in currentEvolution */ market->currentEvolutionItems[i] += (asymptoticNumber - market->numItems[i]) / TYPICAL_TIME; /* Check if new items appeared or disappeared on market */ if (fabs(market->currentEvolutionItems[i]) >= 1.0f) { const int num = (int)(market->currentEvolutionItems[i]); if (num >= 0) BS_AddItemToMarket(od, num); else BS_RemoveItemFromMarket(od, -num); market->currentEvolutionItems[i] -= num; } } for (i = 0; i < AIRCRAFTTYPE_MAX; i++) { const humanAircraftType_t type = (humanAircraftType_t)i; const char* aircraftID = cgi->Com_DropShipTypeToShortName(type); const aircraft_t* aircraft = AIR_GetAircraft(aircraftID); const technology_t* tech = aircraft->tech; int asymptoticNumber; if (RS_IsResearched_ptr(tech) && (campaign->marketDef->numAircraft[i] != 0 || ccs.date.day > tech->researchedDate.day + RESEARCH_LIMIT_DELAY)) { /* if aircraft is researched for more than RESEARCH_LIMIT_DELAY or was on the initial market, * there number tend to the value defined in equipment.ufo. * This value is the asymptotic value if it is not 0, or initial value else */ asymptoticNumber = campaign->asymptoticMarketDef->numAircraft[i] ? campaign->asymptoticMarketDef->numAircraft[i] : campaign->marketDef->numAircraft[i]; } else { /* items that have just been researched don't appear on market, but they can disappear */ asymptoticNumber = 0; } /* Store the evolution of the market in currentEvolution */ market->currentEvolutionAircraft[i] += (asymptoticNumber - market->numAircraft[i]) / TYPICAL_TIME; /* Check if new items appeared or disappeared on market */ if (fabs(market->currentEvolutionAircraft[i]) >= 1.0f) { const int num = (int)(market->currentEvolutionAircraft[i]); if (num >= 0) BS_AddAircraftToMarket(aircraft, num); else BS_RemoveAircraftFromMarket(aircraft, -num); market->currentEvolutionAircraft[i] -= num; } } }