/** * @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); } }
bool CHRSH_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef) { assert(teamDef); assert(INV_IsArmour(od)); if (!teamDef->armour) return false; return od->useable == teamDef->team; }
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 Draw a preview of the DND item dropped into the node */ static void UI_ContainerNodeDrawDropPreview (uiNode_t *target) { item_t previewItem; int checkedTo; vec3_t origine; /* no preview into scrollable list */ if (UI_IsScrollContainerNode(target)) return; /* copy the DND item to not change the original one */ previewItem = *UI_DNDGetItem(); previewItem.rotated = false; checkedTo = INVSH_CheckToInventory(ui_inventory, previewItem.item, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC); switch (checkedTo) { case INV_DOES_NOT_FIT: return; case INV_FITS: break; case INV_FITS_BOTH: /** @todo enable Shift-rotate for battlescape too when issues are solved */ if (!Key_IsDown(K_SHIFT) || CL_BattlescapeRunning()) break; case INV_FITS_ONLY_ROTATED: previewItem.rotated = true; } /* Hack, no preview for armour, we don't want it out of the armour container (and armour container is not visible) */ if (INV_IsArmour(previewItem.item)) return; UI_GetNodeAbsPos(target, origine); origine[2] = -40; /* Get center of single container for placement of preview item */ if (EXTRADATA(target).container->single) { origine[0] += target->box.size[0] / 2.0; origine[1] += target->box.size[1] / 2.0; /* This is a "grid" container - we need to calculate the item-position * (on the screen) from stored placement in the container and the * calculated rotation info. */ } else { if (previewItem.rotated) { origine[0] += (dragInfoToX + previewItem.item->sy / 2.0) * C_UNIT; origine[1] += (dragInfoToY + previewItem.item->sx / 2.0) * C_UNIT; } else { origine[0] += (dragInfoToX + previewItem.item->sx / 2.0) * C_UNIT; origine[1] += (dragInfoToY + previewItem.item->sy / 2.0) * C_UNIT; } } UI_DrawItem(NULL, origine, &previewItem, -1, -1, scale, colorPreview); }
static qboolean GAME_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef) { if (teamDef != NULL && teamDef->armour && INV_IsArmour(od)) { if (CHRSH_IsTeamDefAlien(teamDef)) return od->useable == TEAM_ALIEN; else if (teamDef->race == RACE_PHALANX_HUMAN) return od->useable == TEAM_PHALANX; else if (teamDef->race == RACE_CIVILIAN) return od->useable == TEAM_CIVILIAN; else return qfalse; } return qtrue; }
/** * @brief Returns the head model for the soldiers for armoured and non armoured soldiers * @param[in] chr Pointer to character struct * @sa CHRSH_CharGetBody */ const char *CHRSH_CharGetHead (const character_t * const chr) { static char returnModel[MAX_VAR]; /* models of UGVs don't change - because they are already armoured */ if (INVSH_HasArmour(&chr->i) && chr->teamDef->race != RACE_ROBOT) { const objDef_t *od = INVSH_HasArmour(&chr->i)->item.t; const char *id = od->armourPath; if (!INV_IsArmour(od)) Sys_Error("CHRSH_CharGetBody: Item is no armour"); Com_sprintf(returnModel, sizeof(returnModel), "%s%s/%s", chr->path, id, chr->head); } else Com_sprintf(returnModel, sizeof(returnModel), "%s/%s", chr->path, chr->head); return returnModel; }
bool CHRSH_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef) { assert(teamDef); assert(INV_IsArmour(od)); if (!teamDef->armour) return false; if (CHRSH_IsTeamDefAlien(teamDef)) return od->useable == TEAM_ALIEN; else if (teamDef->race == RACE_PHALANX_HUMAN) return od->useable == TEAM_PHALANX; else if (teamDef->race == RACE_CIVILIAN) return od->useable == TEAM_CIVILIAN; return false; }
qboolean GAME_ItemIsUseable (const objDef_t *od) { const cgame_export_t *list = GAME_GetCurrentType(); if (INV_IsArmour(od)) { const char *teamDefID = GAME_GetTeamDef(); const teamDef_t *teamDef = Com_GetTeamDefinitionByID((teamDefID)); /* Don't allow armour for other teams */ if (!GAME_IsArmourUseableForTeam(od, teamDef)) return qfalse; } if (list && list->IsItemUseable) return list->IsItemUseable(od); return qtrue; }
itemFilterTypes_t INV_GetFilterFromItem (const objDef_t *obj) { assert(obj); /* heavy weapons may be primary too. check heavy first */ if (obj->isHeavy) return FILTER_S_HEAVY; else if (obj->isPrimary) return FILTER_S_PRIMARY; else if (obj->isSecondary) return FILTER_S_SECONDARY; else if (obj->isMisc) return FILTER_S_MISC; else if (INV_IsArmour(obj)) return FILTER_S_ARMOUR; /** @todo need to implement everything */ Sys_Error("INV_GetFilterFromItem: unknown filter category for item '%s'", obj->id); }
/** * @brief Fully equip one actor. The equipment that is added to the inventory of the given actor * is taken from the equipment script definition. * @param[in] self The inventory interface pointer * @param[in] inv The inventory that will get the weapon. * @param[in] ed The equipment that is added from to the actors inventory * @param[in] td Pointer to teamdef data - to get the weapon and armour bools. * @note The code below is a complete implementation * of the scheme sketched at the beginning of equipment_missions.ufo. * Beware: If two weapons in the same category have the same price, * only one will be considered for inventory. */ static void I_EquipActor (inventoryInterface_t* self, inventory_t* const inv, const equipDef_t *ed, const teamDef_t* td) { int i; const int numEquip = lengthof(ed->numItems); int repeat = 0; const float AKIMBO_CHANCE = 0.3; /**< if you got a one-handed secondary weapon (and no primary weapon), this is the chance to get another one (between 0 and 1) */ if (td->weapons) { equipPrimaryWeaponType_t primary = WEAPON_NO_PRIMARY; int sum; int missedPrimary = 0; /**< If actor has a primary weapon, this is zero. Otherwise, this is the probability * 100 * that the actor had to get a primary weapon (used to compensate the lack of primary weapon) */ const objDef_t *primaryWeapon = NULL; int hasWeapon = 0; /* Primary weapons */ const int maxWeaponIdx = min(self->csi->numODs - 1, numEquip - 1); int randNumber = rand() % 100; for (i = 0; i < maxWeaponIdx; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->fireTwoHanded && obj->isPrimary) { randNumber -= ed->numItems[i]; missedPrimary += ed->numItems[i]; if (!primaryWeapon && randNumber < 0) primaryWeapon = obj; } } /* See if a weapon has been selected. */ if (primaryWeapon) { hasWeapon += I_PackAmmoAndWeapon(self, inv, primaryWeapon, 0, ed); if (hasWeapon) { int ammo; /* Find the first possible ammo to check damage type. */ for (ammo = 0; ammo < self->csi->numODs; ammo++) if (ed->numItems[ammo] && INVSH_LoadableInWeapon(&self->csi->ods[ammo], primaryWeapon)) break; if (ammo < self->csi->numODs) { if (/* To avoid two particle weapons. */ !(self->csi->ods[ammo].dmgtype == self->csi->damParticle) /* To avoid SMG + Assault Rifle */ && !(self->csi->ods[ammo].dmgtype == self->csi->damNormal)) { primary = WEAPON_OTHER; } else { primary = WEAPON_PARTICLE_OR_NORMAL; } } /* reset missedPrimary: we got a primary weapon */ missedPrimary = 0; } else { Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: primary weapon '%s' couldn't be equipped in equipment '%s' (%s).\n", primaryWeapon->id, ed->id, self->name); repeat = WEAPONLESS_BONUS > frand(); } } /* Sidearms (secondary weapons with reload). */ do { int randNumber = rand() % 100; const objDef_t *secondaryWeapon = NULL; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->reload && !obj->deplete && obj->isSecondary) { randNumber -= ed->numItems[i] / (primary == WEAPON_PARTICLE_OR_NORMAL ? 2 : 1); if (randNumber < 0) { secondaryWeapon = obj; break; } } } if (secondaryWeapon) { hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, missedPrimary, ed); if (hasWeapon) { /* Try to get the second akimbo pistol if no primary weapon. */ if (primary == WEAPON_NO_PRIMARY && !secondaryWeapon->fireTwoHanded && frand() < AKIMBO_CHANCE) { I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); } } } } while (!hasWeapon && repeat--); /* Misc items and secondary weapons without reload. */ if (!hasWeapon) repeat = WEAPONLESS_BONUS > frand(); else repeat = 0; /* Misc object probability can be bigger than 100 -- you're sure to * have one misc if it fits your backpack */ sum = 0; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && ((obj->weapon && obj->isSecondary && (!obj->reload || obj->deplete)) || obj->isMisc)) { /* if ed->num[i] is greater than 100, the first number is the number of items you'll get: * don't take it into account for probability * Make sure that the probability is at least one if an item can be selected */ sum += ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0; } } if (sum) { do { int randNumber = rand() % sum; const objDef_t *secondaryWeapon = NULL; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && ((obj->weapon && obj->isSecondary && (!obj->reload || obj->deplete)) || obj->isMisc)) { randNumber -= ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0; if (randNumber < 0) { secondaryWeapon = obj; break; } } } if (secondaryWeapon) { int num = ed->numItems[secondaryWeapon->idx] / 100 + (ed->numItems[secondaryWeapon->idx] % 100 >= 100 * frand()); while (num--) { hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); } } } while (repeat--); /* Gives more if no serious weapons. */ } /* If no weapon at all, bad guys will always find a blade to wield. */ if (!hasWeapon) { int maxPrice = 0; const objDef_t *blade = NULL; Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: no weapon picked in equipment '%s', defaulting to the most expensive secondary weapon without reload. (%s)\n", ed->id, self->name); for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->isSecondary && !obj->reload) { if (obj->price > maxPrice) { maxPrice = obj->price; blade = obj; } } } if (maxPrice) hasWeapon += I_PackAmmoAndWeapon(self, inv, blade, 0, ed); } /* If still no weapon, something is broken, or no blades in equipment. */ if (!hasWeapon) Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: cannot add any weapon; no secondary weapon without reload detected for equipment '%s' (%s).\n", ed->id, self->name); /* Armour; especially for those without primary weapons. */ repeat = (float) missedPrimary > frand() * 100.0; } else { return; } if (td->armour) { do { int randNumber = rand() % 100; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *armour = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INV_IsArmour(armour)) { randNumber -= ed->numItems[i]; if (randNumber < 0) { const item_t item = {NONE_AMMO, NULL, armour, 0, 0}; if (self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idArmour])) { repeat = 0; break; } } } } } while (repeat-- > 0); } else { Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: teamdef '%s' may not carry armour (%s)\n", td->name, self->name); } { int randNumber = rand() % 10; for (i = 0; i < self->csi->numODs; i++) { if (ed->numItems[i]) { const objDef_t *miscItem = INVSH_GetItemByIDX(i); if (miscItem->isMisc && !miscItem->weapon) { randNumber -= ed->numItems[i]; if (randNumber < 0) { const qboolean oneShot = miscItem->oneshot; const item_t item = {oneShot ? miscItem->ammo : NONE_AMMO, oneShot ? miscItem : NULL, miscItem, 0, 0}; containerIndex_t container; if (miscItem->headgear) container = self->csi->idHeadgear; else if (miscItem->extension) container = self->csi->idExtension; else container = self->csi->idBackpack; self->TryAddToInventory(self, inv, &item, &self->csi->ids[container]); } } } } } }
/** * @brief Pack a weapon, possibly with some ammo * @param[in] self The inventory interface pointer * @param[in] inv The inventory that will get the weapon * @param[in] weapon The weapon type index in gi.csi->ods * @param[in] ed The equipment for debug messages * @param[in] missedPrimary if actor didn't get primary weapon, this is 0-100 number to increase ammo number. * @sa INVSH_LoadableInWeapon */ static int I_PackAmmoAndWeapon (inventoryInterface_t *self, inventory_t* const inv, const objDef_t* weapon, int missedPrimary, const equipDef_t *ed) { const objDef_t *ammo = NULL; item_t item = {NONE_AMMO, NULL, NULL, 0, 0}; qboolean allowLeft; qboolean packed; int ammoMult = 1; assert(!INV_IsArmour(weapon)); item.t = weapon; /* are we going to allow trying the left hand */ allowLeft = !(inv->c[self->csi->idRight] && inv->c[self->csi->idRight]->item.t->fireTwoHanded); if (weapon->oneshot) { /* The weapon provides its own ammo (i.e. it is charged or loaded in the base.) */ item.a = weapon->ammo; item.m = weapon; Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: oneshot weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); } else if (!weapon->reload) { item.m = item.t; /* no ammo needed, so fire definitions are in t */ } else { /* find some suitable ammo for the weapon (we will have at least one if there are ammos for this * weapon in equipment definition) */ int totalAvailableAmmo = 0; int i; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { totalAvailableAmmo++; } } if (totalAvailableAmmo) { int randNumber = rand() % totalAvailableAmmo; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { randNumber--; if (randNumber < 0) { ammo = obj; break; } } } } if (!ammo) { Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); return 0; } /* load ammo */ item.a = weapon->ammo; item.m = ammo; } if (!item.m) { Com_Printf("I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); return 0; } /* now try to pack the weapon */ packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idRight]); if (packed) ammoMult = 3; if (!packed && allowLeft) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idLeft]); if (!packed) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idBelt]); if (!packed) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idHolster]); if (!packed) return 0; /* pack some more ammo in the backpack */ if (ammo) { int num; int numpacked = 0; /* how many clips? */ num = (1 + ed->numItems[ammo->idx]) * (float) (1.0f + missedPrimary / 100.0); /* pack some ammo */ while (num--) { item_t mun = {NONE_AMMO, NULL, NULL, 0, 0}; mun.t = ammo; /* ammo to backpack; belt is for knives and grenades */ numpacked += self->TryAddToInventory(self, inv, &mun, &self->csi->ids[self->csi->idBackpack]); /* no problem if no space left; one ammo already loaded */ if (numpacked > ammoMult || numpacked * weapon->ammo > 11) break; } } return qtrue; }
/** * @brief Conditions for moving items between containers. * @param[in] self The inventory interface pointer * @param[in] inv Inventory to move in. * @param[in] from Source container. * @param[in] fItem The item to be moved. * @param[in] to Destination container. * @param[in] tx X coordinate in destination container. * @param[in] ty Y coordinate in destination container. * @param[in,out] TU pointer to entity available TU at this moment * or @c NULL if TU doesn't matter (outside battlescape) * @param[out] icp * @return IA_NOTIME when not enough TU. * @return IA_NONE if no action possible. * @return IA_NORELOAD if you cannot reload a weapon. * @return IA_RELOAD_SWAP in case of exchange of ammo in a weapon. * @return IA_RELOAD when reloading. * @return IA_ARMOUR when placing an armour on the actor. * @return IA_MOVE when just moving an item. */ static int I_MoveInInventory (inventoryInterface_t* self, inventory_t* const inv, const invDef_t * from, invList_t *fItem, const invDef_t * to, int tx, int ty, int *TU, invList_t ** icp) { invList_t *ic; int time; int checkedTo = INV_DOES_NOT_FIT; qboolean alreadyRemovedSource = qfalse; assert(to); assert(from); if (icp) *icp = NULL; if (from == to && fItem->x == tx && fItem->y == ty) return IA_NONE; time = from->out + to->in; if (from == to) { if (INV_IsFloorDef(from)) time = 0; else time /= 2; } if (TU && *TU < time) return IA_NOTIME; assert(inv); /* Special case for moving an item within the same container. */ if (from == to) { /* Do nothing if we move inside a scroll container. */ if (from->scroll) return IA_NONE; ic = inv->c[from->id]; for (; ic; ic = ic->next) { if (ic == fItem) { if (ic->item.amount > 1) { checkedTo = INVSH_CheckToInventory(inv, ic->item.t, to, tx, ty, fItem); if (checkedTo & INV_FITS) { ic->x = tx; ic->y = ty; if (icp) *icp = ic; return IA_MOVE; } return IA_NONE; } } } } /* If weapon is twohanded and is moved from hand to hand do nothing. */ /* Twohanded weapon are only in CSI->idRight. */ if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to) && INV_IsRightDef(from)) { return IA_NONE; } /* If non-armour moved to an armour slot then abort. * Same for non extension items when moved to an extension slot. */ if ((to->armour && !INV_IsArmour(fItem->item.t)) || (to->extension && !fItem->item.t->extension) || (to->headgear && !fItem->item.t->headgear)) { return IA_NONE; } /* Check if the target is a blocked inv-armour and source!=dest. */ if (to->single) checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, 0, 0, fItem); else { if (tx == NONE || ty == NONE) INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); /* still no valid location found */ if (tx == NONE || ty == NONE) return IA_NONE; checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, tx, ty, fItem); } if (to->armour && from != to && !checkedTo) { item_t cacheItem2; invList_t *icTo; /* Store x/y origin coordinates of removed (source) item. * When we re-add it we can use this. */ const int cacheFromX = fItem->x; const int cacheFromY = fItem->y; /* Check if destination/blocking item is the same as source/from item. * In that case the move is not needed -> abort. */ icTo = INVSH_SearchInInventory(inv, to, tx, ty); if (fItem->item.t == icTo->item.t) return IA_NONE; /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; else /* Removal successful - store this info. */ alreadyRemovedSource = qtrue; cacheItem2 = self->cacheItem; /* Save/cache (source) item. The cacheItem is modified in I_MoveInInventory. */ /* Move the destination item to the source. */ self->MoveInInventory(self, inv, to, icTo, from, cacheFromX, cacheFromY, TU, icp); /* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */ self->cacheItem = cacheItem2; } else if (!checkedTo) { /* Get the target-invlist (e.g. a weapon). We don't need to check for * scroll because checkedTo is always true here. */ ic = INVSH_SearchInInventory(inv, to, tx, ty); if (ic && !INV_IsEquipDef(to) && INVSH_LoadableInWeapon(fItem->item.t, ic->item.t)) { /* A target-item was found and the dragged item (implicitly ammo) * can be loaded in it (implicitly weapon). */ if (ic->item.a >= ic->item.t->ammo && ic->item.m == fItem->item.t) { /* Weapon already fully loaded with the same ammunition -> abort */ return IA_NORELOAD; } time += ic->item.t->reload; if (!TU || *TU >= time) { if (TU) *TU -= time; if (ic->item.a >= ic->item.t->ammo) { /* exchange ammo */ const item_t item = {NONE_AMMO, NULL, ic->item.m, 0, 0}; /* Put current ammo in place of the new ammo unless floor - there can be more than 1 item */ const int cacheFromX = INV_IsFloorDef(from) ? NONE : fItem->x; const int cacheFromY = INV_IsFloorDef(from) ? NONE : fItem->y; /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; /* Add the currently used ammo in place of the new ammo in the "from" container. */ if (self->AddToInventory(self, inv, &item, from, cacheFromX, cacheFromY, 1) == NULL) Sys_Error("Could not reload the weapon - add to inventory failed (%s)", self->name); ic->item.m = self->cacheItem.t; if (icp) *icp = ic; return IA_RELOAD_SWAP; } else { /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; ic->item.m = self->cacheItem.t; /* loose ammo of type ic->item.m saved on server side */ ic->item.a = ic->item.t->ammo; if (icp) *icp = ic; return IA_RELOAD; } } /* Not enough time -> abort. */ return IA_NOTIME; } /* temp container like idEquip and idFloor */ if (ic && to->temp) { /* We are moving to a blocked location container but it's the base-equipment floor or a battlescape floor. * We add the item anyway but it'll not be displayed (yet) * This is then used in I_AddToInventory below.*/ /** @todo change the other code to browse trough these things. */ INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); if (tx == NONE || ty == NONE) { Com_DPrintf(DEBUG_SHARED, "I_MoveInInventory - item will be added non-visible (%s)\n", self->name); } } else { /* Impossible move -> abort. */ return IA_NONE; } } /* twohanded exception - only CSI->idRight is allowed for fireTwoHanded weapons */ if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to)) to = &self->csi->ids[self->csi->idRight]; if (checkedTo == INV_FITS_ONLY_ROTATED) { /* Set rotated tag */ fItem->item.rotated = qtrue; } else if (fItem->item.rotated) { /* Remove rotated tag */ fItem->item.rotated = qfalse; } /* Actually remove the item from the 'from' container (if it wasn't already removed). */ if (!alreadyRemovedSource) if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; /* successful */ if (TU) *TU -= time; assert(self->cacheItem.t); ic = self->AddToInventory(self, inv, &self->cacheItem, to, tx, ty, 1); /* return data */ if (icp) { assert(ic); *icp = ic; } if (INV_IsArmourDef(to)) { assert(INV_IsArmour(self->cacheItem.t)); return IA_ARMOUR; } else return IA_MOVE; }
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) { nametypes_t l; for (l = NAME_NEUTRAL; l < NAME_LAST; l++) { linkedList_t *list = teamDef->models[l]; int k; /* no models for this gender */ if (!teamDef->numModels[l]) continue; CU_ASSERT_PTR_NOT_NULL(list); for (k = 0; k < teamDef->numModels[l]; k++) { const char *path; CU_ASSERT_PTR_NOT_NULL_FATAL(list); path = (const char*)list->data; /* body */ list = list->next; CU_ASSERT_PTR_NOT_NULL_FATAL(list); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", path, list->data)), va("%s does not exist in models/%s (teamDef: %s)", list->data, path, teamDef->id)); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", path, armourPath, list->data)), va("%s does not exist in models/%s%s (teamDef: %s)", list->data, path, armourPath, teamDef->id)); list = list->next; CU_ASSERT_PTR_NOT_NULL_FATAL(list); /* head */ UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", path, list->data)), va("%s does not exist in models/%s (teamDef: %s)", list->data, path, teamDef->id)); UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", path, armourPath, list->data)), va("%s does not exist in models/%s%s (teamDef: %s)", list->data, path, armourPath, teamDef->id)); /* skip skin */ /** @todo check that the skin is valid for the given model */ list = list->next; CU_ASSERT_PTR_NOT_NULL_FATAL(list); /* new path */ list = list->next; } } } LIST_Delete(&armourPaths); }
/** * @brief Prints the description for items (weapons, armour, ...) * @param[in] od The object definition of the item * @note Not only called from UFOpaedia but also from other places to display * weapon and ammo statistics * @todo Do we need to add checks for @c od->isDummy here somewhere? */ void INV_ItemDescription (const objDef_t *od) { static char itemText[UI_MAX_SMALLTEXTLEN]; int i; int count; currentDisplayedObject = od; if (!od) { /* If nothing selected return */ Cvar_Set("mn_itemname", ""); Cvar_Set("mn_item", ""); UI_ResetData(TEXT_ITEMDESCRIPTION); itemIndex = fireModeIndex = 0; UI_ExecuteConfunc("itemdesc_view 0 0;"); return; } /* select item */ Cvar_Set("mn_itemname", _(od->name)); Cvar_Set("mn_item", od->id); count = 0; if (INV_IsAmmo(od)) { /* We display the pre/next buttons for changing weapon only if there are at least 2 researched weapons * we are counting the number of weapons that are usable with this ammo */ for (i = 0; i < od->numWeapons; i++) if (GAME_ItemIsUseable(od->weapons[i])) count++; if (itemIndex >= od->numWeapons || itemIndex < 0) itemIndex = 0; if (count > 0) { while (!GAME_ItemIsUseable(od->weapons[itemIndex])) { itemIndex++; if (itemIndex >= od->numWeapons) itemIndex = 0; } Cvar_ForceSet("mn_linkname", _(od->weapons[itemIndex]->name)); } } else if (od->weapon && od->reload) { /* We display the pre/next buttons for changing ammo only if there are at least 2 researched ammo * we are counting the number of ammo that is usable with this weapon */ for (i = 0; i < od->numAmmos; i++) if (GAME_ItemIsUseable(od->ammos[i])) count++; if (itemIndex >= od->numAmmos || itemIndex < 0) itemIndex = 0; /* Only display ammos if at least one has been researched */ if (count > 0) { /* We have a weapon that uses ammos */ while (!GAME_ItemIsUseable(od->ammos[itemIndex])) { itemIndex++; if (itemIndex >= od->numAmmos) itemIndex = 0; } Cvar_ForceSet("mn_linkname", _(od->ammos[itemIndex]->name)); } } /* set description text if item has been researched or one of its ammo/weapon has been researched */ if (count > 0 || GAME_ItemIsUseable(od)) { int numFiredefs = 0; *itemText = '\0'; if (INV_IsArmour(od)) { Com_sprintf(itemText, sizeof(itemText), _("Size:\t%i\n"), od->size); Q_strcat(itemText, "\n", sizeof(itemText)); Q_strcat(itemText, _("^BDamage type:\tProtection:\n"), sizeof(itemText)); for (i = 0; i < csi.numDTs; i++) { const damageType_t *dt = &csi.dts[i]; if (!dt->showInMenu) continue; Q_strcat(itemText, va(_("%s\t%i\n"), _(dt->id), od->ratings[i]), sizeof(itemText)); } } else if ((od->weapon && od->numAmmos) || INV_IsAmmo(od)) { const objDef_t *odAmmo; if (count > 0) { int weaponIndex; if (od->weapon) { Com_sprintf(itemText, sizeof(itemText), _("%s weapon\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); if (od->ammo > 0) Q_strcat(itemText, va(_("Max ammo:\t%i\n"), od->ammo), sizeof(itemText)); odAmmo = (od->numAmmos) ? od->ammos[itemIndex] : od; assert(odAmmo); for (weaponIndex = 0; (weaponIndex < odAmmo->numWeapons) && (odAmmo->weapons[weaponIndex] != od); weaponIndex++) {} } else { odAmmo = od; weaponIndex = itemIndex; } /** @todo is there ammo with no firedefs? */ if (GAME_ItemIsUseable(odAmmo) && odAmmo->numFiredefs[weaponIndex] > 0) { const fireDef_t *fd; numFiredefs = odAmmo->numFiredefs[weaponIndex]; /* This contains everything common for weapons and ammos */ /* We check if the wanted firemode to display exists. */ if (fireModeIndex > numFiredefs - 1) fireModeIndex = 0; if (fireModeIndex < 0) fireModeIndex = numFiredefs - 1; fd = &odAmmo->fd[weaponIndex][fireModeIndex]; /* We always display the name of the firemode for an ammo */ Cvar_Set("mn_firemodename", _(fd->name)); /* We display the characteristics of this firemode */ Q_strcat(itemText, va(_("Skill:\t%s\n"), CL_WeaponSkillToName(fd->weaponSkill)), sizeof(itemText)); Q_strcat(itemText, va(_("Damage:\t%i\n"), (int) (fd->damage[0] + fd->spldmg[0]) * fd->shots), sizeof(itemText)); Q_strcat(itemText, va(_("Time units:\t%i\n"), fd->time), sizeof(itemText)); Q_strcat(itemText, va(_("Range:\t%g\n"), fd->range / UNIT_SIZE), sizeof(itemText)); Q_strcat(itemText, va(_("Spreads:\t%g\n"), (fd->spread[0] + fd->spread[1]) / 2), sizeof(itemText)); } } else { Com_sprintf(itemText, sizeof(itemText), _("%s. No detailed info available.\n"), INV_IsAmmo(od) ? _("Ammunition") : _("Weapon")); } } else if (od->weapon) { Com_sprintf(itemText, sizeof(itemText), _("%s ammo-less weapon\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); } else { /* just an item - only primary definition */ Com_sprintf(itemText, sizeof(itemText), _("%s auxiliary equipment\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); if (od->numWeapons > 0 && od->numFiredefs[0] > 0) { const fireDef_t *fd = &od->fd[0][0]; Q_strcat(itemText, va(_("Action:\t%s\n"), _(fd->name)), sizeof(itemText)); Q_strcat(itemText, va(_("Time units:\t%i\n"), fd->time), sizeof(itemText)); Q_strcat(itemText, va(_("Range:\t%g\n"), fd->range / UNIT_SIZE), sizeof(itemText)); } } UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); UI_ExecuteConfunc("itemdesc_view %i %i;", count, numFiredefs); } else { Com_sprintf(itemText, sizeof(itemText), _("Unknown - not useable")); UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); UI_ExecuteConfunc("itemdesc_view 0 0;"); } }
/** * @brief Checks if the given object/item matched the given filter type. * @param[in] obj A pointer to an objDef_t item. * @param[in] filterType Filter type to check against. * @return @c true if obj is in filterType */ bool INV_ItemMatchesFilter (const objDef_t *obj, const itemFilterTypes_t filterType) { int i; if (!obj) return false; switch (filterType) { case FILTER_S_PRIMARY: if (obj->isPrimary && !obj->isHeavy) return true; /* Check if one of the items that uses this ammo matches this filter type. */ for (i = 0; i < obj->numWeapons; i++) { const objDef_t *weapon = obj->weapons[i]; if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType)) return true; } break; case FILTER_S_SECONDARY: if (obj->isSecondary && !obj->isHeavy) return true; /* Check if one of the items that uses this ammo matches this filter type. */ for (i = 0; i < obj->numWeapons; i++) { const objDef_t *weapon = obj->weapons[i]; if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType)) return true; } break; case FILTER_S_HEAVY: if (obj->isHeavy) return true; /* Check if one of the items that uses this ammo matches this filter type. */ for (i = 0; i < obj->numWeapons; i++) { const objDef_t *weapon = obj->weapons[i]; if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType)) return true; } break; case FILTER_S_ARMOUR: return INV_IsArmour(obj); case FILTER_S_MISC: return obj->isMisc; case FILTER_CRAFTITEM: /** @todo Should we handle FILTER_AIRCRAFT here as well? */ return INV_IsCraftItem(obj); case FILTER_UGVITEM: return obj->isUGVitem; case FILTER_DUMMY: return obj->isDummy; case FILTER_AIRCRAFT: return Q_streq(obj->type, "aircraft"); case FILTER_DISASSEMBLY: /** @todo I guess we should search for components matching this item here. */ break; case MAX_SOLDIER_FILTERTYPES: case MAX_FILTERTYPES: case FILTER_ENSURE_32BIT: Com_Printf("INV_ItemMatchesFilter: Unknown filter type for items: %i\n", filterType); break; } /* The given filter type is unknown. */ return false; }
/** * @brief Try to autoplace an item from a container. * @param[in] node The context node * @param[in] ic An item from the node container * @todo We should use an item ID, and get the item inside the function, to avoid wrong uses. */ void UI_ContainerNodeAutoPlaceItem (uiNode_t* node, invList_t *ic) { containerIndex_t target; uiNode_t *targetNode; bool ammoChanged = false; const invDef_t *container = EXTRADATA(node).container; /* Right click: automatic item assignment/removal. */ if (container->id != csi.idEquip) { if (ic->item.ammo && ic->item.ammo != ic->item.item && ic->item.ammoLeft) { /* Remove ammo on removing weapon from a soldier */ target = csi.idEquip; ammoChanged = INV_UnloadWeapon(ic, ui_inventory, INVDEF(target)); } else { /* Move back to idEquip (ground, floor) container. */ target = csi.idEquip; INV_MoveItem(ui_inventory, INVDEF(target), NONE, NONE, container, ic, NULL); } } else { bool packed = false; assert(ic->item.item); /* armour can only have one target */ if (INV_IsArmour(ic->item.item)) { target = csi.idArmour; packed = INV_MoveItem(ui_inventory, INVDEF(target), 0, 0, container, ic, NULL); /* ammo or item */ } else if (INV_IsAmmo(ic->item.item)) { /* Finally try left and right hand. There is no other place to put it now. */ const containerIndex_t idxArray[] = { csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft, csi.idRight }; const size_t size = lengthof(idxArray); unsigned int i; for (i = 0; i < size; i++) { target = idxArray[i]; packed = UI_ContainerNodeAddItem(container, ic, target, NULL); if (packed) break; } } else { if (ic->item.item->headgear) { target = csi.idHeadgear; packed = UI_ContainerNodeAddItem(container, ic, target, NULL); } else { /* left and right are single containers, but this might change - it's cleaner to check * for available space here, too */ const containerIndex_t idxArray[] = { csi.idRight, csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft }; const size_t size = lengthof(idxArray); invList_t *tItem = NULL; unsigned int i; for (i = 0; i < size; i++) { target = idxArray[i]; packed = UI_ContainerNodeAddItem(container, ic, target, &tItem); if (packed) { if ((ic->item.item->weapon && !ic->item.ammoLeft) || ic->item.item->oneshot) ammoChanged = INV_LoadWeapon(tItem, ui_inventory, container, INVDEF(target)); break; } } } } /* no need to continue here - placement wasn't successful at all */ if (!packed) return; } /* Run onChange events */ targetNode = UI_GetContainerNodeByContainerIDX(node->parent, target); if (node->onChange) UI_ExecuteEventActions(node, node->onChange); if (targetNode != NULL && node != targetNode && targetNode->onChange) UI_ExecuteEventActions(targetNode, targetNode->onChange); /* Also call onChange for equip_ammo if ammo moved * Maybe there's a better way to do this? */ if (INV_IsAmmo(ic->item.item) || ammoChanged) { /** @todo hard coded node name, remove it when it is possible */ uiNode_t *ammoNode = UI_GetNode(node->root, "equip_ammo"); if (ammoNode != NULL && node != ammoNode && ammoNode->onChange) UI_ExecuteEventActions(ammoNode, ammoNode->onChange); } }