/** * @brief Debug function to print a player's inventory */ void G_InvList_f (const Player& player) { Edict* ent = nullptr; gi.DPrintf("Print inventory for '%s'\n", player.pers.netname); while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, player.getTeam()))) { gi.DPrintf("actor: '%s'\n", ent->chr.name); const Container* cont = nullptr; while ((cont = ent->chr.inv.getNextCont(cont, true))) { Com_Printf("Container: %i\n", cont->id); Item* item = nullptr; while ((item = cont->getNextItem(item))) { Com_Printf(".. item.def(): %i, item.ammo: %i, item.ammoLeft: %i, x: %i, y: %i\n", (item->def() ? item->def()->idx : NONE), (item->ammoDef() ? item->ammoDef()->idx : NONE), item->getAmmoLeft(), item->getX(), item->getY()); if (item->def()) Com_Printf(".... weapon: %s\n", item->def()->id); if (item->ammoDef()) Com_Printf(".... ammo: %s (%i)\n", item->ammoDef()->id, item->getAmmoLeft()); } } const float invWeight = ent->chr.inv.getWeight(); const int maxWeight = ent->chr.score.skills[ABILITY_POWER]; const float penalty = GET_ENCUMBRANCE_PENALTY(invWeight, maxWeight); const int normalTU = GET_TU(ent->chr.score.skills[ABILITY_SPEED], 1.0f - WEIGHT_NORMAL_PENALTY); const int tus = GET_TU(ent->chr.score.skills[ABILITY_SPEED], penalty); const int tuPenalty = tus - normalTU; const char* penaltyStr = 1.0f - penalty < WEIGHT_NORMAL_PENALTY ? "'Light weight'" : (1.0f - penalty < WEIGHT_HEAVY_PENALTY ? "'Normal weight'" : "'Encumbered'"); Com_Printf("Weight: %g/%i, Encumbrance: %s (%.0f%%), TU's: %i (normal: %i, penalty/bonus: %+i)\n", invWeight, maxWeight, penaltyStr, invWeight / maxWeight * 100.0f, tus, normalTU, tuPenalty); } }
TEST_F(GameTest, InventoryForDiedAlien) { const char* mapName = "test_game"; ASSERT_NE(-1, FS_CheckFile("maps/%s.bsp", mapName)) << "Map resource '" << mapName << ".bsp' for test is missing."; Actor* diedEnt; Actor* actor; Edict* floorItems; Item* invlist; int count; SV_Map(true, mapName, nullptr); level.activeTeam = TEAM_ALIEN; /* first alien that should die and drop its inventory */ diedEnt = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN); ASSERT_TRUE(nullptr != diedEnt); diedEnt->HP = 0; ASSERT_TRUE(G_ActorDieOrStun(diedEnt, nullptr)); ASSERT_TRUE(diedEnt->isDead()); /* now try to collect the inventory with a second alien */ actor = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN); ASSERT_TRUE(nullptr != actor); /* move to the location of the first died alien to drop the inventory into the same floor container */ Player& player = actor->getPlayer(); ASSERT_TRUE(G_IsAIPlayer(&player)); G_ClientMove(player, 0, actor, diedEnt->pos); ASSERT_TRUE(VectorCompare(actor->pos, diedEnt->pos)); floorItems = G_GetFloorItems(actor); ASSERT_TRUE(nullptr != floorItems); ASSERT_EQ(floorItems->getFloor(), actor->getFloor()); /* drop everything to floor to make sure we have space in the backpack */ G_InventoryToFloor(actor); ASSERT_EQ(0, GAMETEST_GetItemCount(actor, CID_BACKPACK)); invlist = actor->getContainer(CID_BACKPACK); ASSERT_TRUE(nullptr == invlist); count = GAMETEST_GetItemCount(actor, CID_FLOOR); if (count > 0) { Item* entryToMove = actor->getFloor(); int tx, ty; actor->chr.inv.findSpace(INVDEF(CID_BACKPACK), entryToMove, &tx, &ty, entryToMove); if (tx == NONE) return; Com_Printf("trying to move item %s from floor into backpack to pos %i:%i\n", entryToMove->def()->name, tx, ty); ASSERT_TRUE(G_ActorInvMove(actor, INVDEF(CID_FLOOR), entryToMove, INVDEF(CID_BACKPACK), tx, ty, false)); ASSERT_EQ(count - 1, GAMETEST_GetItemCount(actor, CID_FLOOR)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack"; Com_Printf("item %s was removed from floor\n", entryToMove->def()->name); ASSERT_EQ(1, GAMETEST_GetItemCount(actor, CID_BACKPACK)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack"; Com_Printf("item %s was moved successfully into the backpack\n", entryToMove->def()->name); invlist = actor->getContainer(CID_BACKPACK); ASSERT_TRUE(nullptr != invlist); } }
/** * @sa CL_InvDel * @sa G_SendInventory * @sa EV_INV_ADD */ void CL_InvAdd (const eventRegister_t* self, dbuffer* msg) { const int number = NET_ReadShort(msg); le_t* le = LE_Get(number); int nr = NET_ReadShort(msg); if (!le) LE_NotFoundError(number); le->flags &= ~LE_REMOVE_NEXT_FRAME; for (; nr-- > 0;) { Item item; containerIndex_t container; int x, y; CL_NetReceiveItem(msg, &item, &container, &x, &y); if (LE_IsItem(le)) { if (container != CID_FLOOR) Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container); } else if (INVDEF(container)->temp) { Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container); } if (cls.i.addToInventory(&le->inv, &item, INVDEF(container), x, y, item.getAmount()) == nullptr) Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i", item.getAmount(), item.def()->id, container); if (container == CID_RIGHT) le->right = item.def()->idx; else if (container == CID_LEFT) le->left = item.def()->idx; else if (container == CID_HEADGEAR) le->headgear = item.def()->idx; } switch (le->type) { case ET_ACTOR: case ET_ACTOR2x2: if (LE_IsSelected(le)) Cmd_ExecuteString("hud_updateactorload"); LE_SetThink(le, LET_StartIdle); break; case ET_ITEM: LE_PlaceItem(le); break; default: break; } }
/** * @brief Updates the character cvars for the given character. * * The models and stats that are displayed in the menu are stored in cvars. * These cvars are updated here when you select another character. * * @param[in] chr Pointer to character_t (may not be null) * @sa CL_UGVCvars * @sa CL_ActorSelect */ static void CL_ActorCvars (const character_t* chr) { Item* weapon; assert(chr); /* visible equipment */ weapon = chr->inv.getRightHandContainer(); if (weapon) Cvar_Set("mn_rweapon", "%s", weapon->def()->model); else Cvar_Set("mn_rweapon", ""); weapon = chr->inv.getLeftHandContainer(); if (weapon) Cvar_Set("mn_lweapon", "%s", weapon->def()->model); else Cvar_Set("mn_lweapon", ""); }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @todo use level.actualRound to determine the 'King of the Hill' time * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!self->owner()) return false; switch (self->owner()->getTeam()) { case TEAM_ALIEN: if (G_IsAlien(activator)) { if (!self->count) { self->count = level.actualRound; gi.BroadcastPrintf(PRINT_HUD, _("Aliens entered target zone!")); } return true; } else { /* reset king of the hill counter */ self->count = 0; } /* general case that also works for multiplayer teams */ default: if (!activator->isSameTeamAs(self->owner())) { /* reset king of the hill counter */ self->count = 0; return false; } if (self->owner()->count) return false; self->owner()->count = level.actualRound; if (!self->owner()->item) { gi.BroadcastPrintf(PRINT_HUD, _("Target zone is occupied!")); return true; } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = activator->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->owner()->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(makeActor(activator), cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); gi.BroadcastPrintf(PRINT_HUD, _("Item was placed.")); self->owner()->count = level.actualRound; return true; } } break; } return true; }
/** * @brief Add an item to a specified container in a given inventory. * @note Set x and y to NONE if the item should get added to an automatically chosen free spot in the container. * @param[in,out] inv Pointer to inventory definition, to which we will add item. * @param[in] item Item to add to given container (needs to have "rotated" tag already set/checked, this is NOT checked here!) * @param[in] container Container in given inventory definition, where the new item will be stored. * @param[in] x,y The location in the container. * @param[in] amount How many items of this type should be added. (this will overwrite the amount as defined in "item.amount") * @sa removeFromInventory * @return the @c Item pointer the item was added to, or @c nullptr in case of an error (item wasn't added) */ Item* InventoryInterface::addToInventory (Inventory* const inv, const Item* const item, const invDef_t* container, int x, int y, int amount) { if (!item->def()) return nullptr; if (amount <= 0) return nullptr; assert(inv); assert(container); if (container->single && inv->getContainer2(container->id)) return nullptr; Item* ic; /* CID_EQUIP and CID_FLOOR */ if (container->temp) { for (ic = inv->getContainer2(container->id); ic; ic = ic->getNext()) if (ic->isSameAs(item)) { ic->addAmount(amount); Com_DPrintf(DEBUG_SHARED, "addToInventory: Amount of '%s': %i (%s)\n", ic->def()->name, ic->getAmount(), invName); return ic; } } if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH || y >= SHAPE_BIG_MAX_HEIGHT) { /* No (sane) position in container given as parameter - find free space on our own. */ inv->findSpace(container, item, &x, &y, nullptr); if (x == NONE) return nullptr; } const int checkedTo = inv->canHoldItem(container, item->def(), x, y, nullptr); assert(checkedTo); /* not found - add a new one */ ic = addInvList(inv, container); /* Set the data in the new entry to the data we got via function-parameters.*/ *ic = *item; ic->setNext(nullptr); ic->setAmount(amount); /* don't reset an already applied rotation */ if (checkedTo == INV_FITS_ONLY_ROTATED) ic->rotated = true; ic->setX(x); ic->setY(y); return ic; }
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 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 Reload weapon with actor. * @param[in] ent Pointer to an actor reloading weapon. * @param[in] invDef Reloading weapon in right or left hand. * @sa AI_ActorThink */ void G_ActorReload (Edict* ent, const invDef_t* invDef) { const objDef_t* weapon; if (ent->getContainer(invDef->id)) { weapon = ent->getContainer(invDef->id)->def(); } else if (invDef->isLeftDef() && ent->getRightHandItem()->isHeldTwoHanded()) { /* Check for two-handed weapon */ invDef = INVDEF(CID_RIGHT); weapon = ent->getRightHandItem()->def(); } else return; assert(weapon); /* LordHavoc: Check if item is researched when in singleplayer? I don't think this is really a * cheat issue as in singleplayer there is no way to inject fake client commands in the virtual * network buffer, and in multiplayer everything is researched */ /* search for clips and select the one that is available easily */ /* also try the temp containers */ const invDef_t* bestContainer = nullptr; Item* ammoItem = nullptr; int tu = 100; const Container* cont = nullptr; while ((cont = ent->chr.inv.getNextCont(cont, true))) { if (cont->def()->out >= tu) continue; /* Once we've found at least one clip, there's no point * searching other containers if it would take longer * to retrieve the ammo from them than the one * we've already found. */ Item* item = nullptr; while ((item = cont->getNextItem(item))) { if (item->def()->isLoadableInWeapon(weapon)) { ammoItem = item; bestContainer = INVDEF(cont->id); tu = bestContainer->out; break; } } } /* send request */ if (bestContainer) G_ActorInvMove(ent, bestContainer, ammoItem, invDef, 0, 0, true); }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!G_IsLivingActor(activator)) return false; Actor* actor = makeActor(activator); const char* const actorTeam = G_MissionGetTeamString(actor->getTeam()); if (!G_IsCivilian(actor) && self->isOpponent(actor)) { if (!self->item && self->count) { if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname); } else { const char* const teamName = G_MissionGetTeamString(self->getTeam()); gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"), actorTeam, teamName); } /* reset king of the hill counter */ self->count = 0; } return false; } if (self->count) return false; if (self->isSameTeamAs(actor)) { self->count = level.actualRound; if (!self->item) { linkedList_t* touched = self->touchedList; while (touched) { const Edict* const ent = static_cast<const Edict* const>(touched->data); if (!self->isSameTeamAs(ent) && !G_IsDead(ent)) { return true; } touched = touched->next; } if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam); } return true; } } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = actor->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name); } self->count = level.actualRound; return true; } } return false; }
/** * @brief Conditions for moving items between containers. * @param[in] inv The 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 nullptr if TU doesn't matter (outside battlescape) * @param[out] uponItem The item fItem is evetually dropped upon * @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. */ inventory_action_t InventoryInterface::moveInInventory (Inventory* const inv, const invDef_t* from, Item* fItem, const invDef_t* to, int tx, int ty, int* TU, Item** uponItem) { assert(to); assert(from); if (uponItem) *uponItem = nullptr; if (from == to && fItem->getX() == tx && fItem->getY() == ty) return IA_NONE; int time = from->out + to->in; if (from == to) { if (from->isFloorDef()) time = 0; else time /= 2; } if (TU && *TU < time) return IA_NOTIME; assert(inv); int checkedTo = INV_DOES_NOT_FIT; /* 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; const Container& cont = inv->getContainer(from->id); Item* item = nullptr; while ((item = cont.getNextItem(item))) { if (item != fItem) continue; if (item->getAmount() <= 1) continue; checkedTo = inv->canHoldItem(to, item->def(), tx, ty, fItem); if (!(checkedTo & INV_FITS)) return IA_NONE; item->setX(tx); item->setY(ty); if (uponItem) *uponItem = item; return IA_MOVE; } } /* If weapon is twohanded and is moved from hand to hand do nothing. */ /* Twohanded weapon are only in CID_RIGHT. */ if (fItem->def()->fireTwoHanded && to->isLeftDef() && from->isRightDef()) { 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 && !fItem->isArmour()) || (to->implant && !fItem->def()->implant) || (to->headgear && !fItem->def()->headgear)) { return IA_NONE; } /* Check if the target is a blocked inv-armour and source!=dest. */ if (to->single) checkedTo = inv->canHoldItem(to, fItem->def(), 0, 0, fItem); else { if (tx == NONE || ty == NONE) inv->findSpace(to, fItem, &tx, &ty, fItem); /* still no valid location found */ if (tx == NONE || ty == NONE) return IA_NONE; checkedTo = inv->canHoldItem(to, fItem->def(), tx, ty, fItem); } Item* ic; bool alreadyRemovedSource = false; if (to->armour && from != to && !checkedTo) { /* Store x/y origin coordinates of removed (source) item. * When we re-add it we can use this. */ const int cacheFromX = fItem->getX(); const int cacheFromY = fItem->getY(); /* Check if destination/blocking item is the same as source/from item. * In that case the move is not needed -> abort. */ Item* icTo = inv->getItemAtPos(to, tx, ty); if (fItem->def() == icTo->def()) return IA_NONE; /* Actually remove the ammo from the 'from' container. */ if (!removeFromInventory(inv, from, fItem)) return IA_NONE; else /* Removal successful - store this info. */ alreadyRemovedSource = true; Item cacheItem2 = this->cacheItem; /* Save/cache (source) item. The cacheItem is modified in MoveInInventory. */ /* Move the destination item to the source. */ moveInInventory(inv, to, icTo, from, cacheFromX, cacheFromY, TU, uponItem); /* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */ this->cacheItem = cacheItem2; checkedTo = inv->canHoldItem(to, this->cacheItem.def(), 0, 0, fItem); } 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 = inv->getItemAtPos(to, tx, ty); if (ic && !to->isEquipDef() && fItem->def()->isLoadableInWeapon(ic->def())) { /* A target-item was found and the dragged item (implicitly ammo) * can be loaded in it (implicitly weapon). */ if (ic->getAmmoLeft() >= ic->def()->ammo && ic->ammoDef() == fItem->def()) { /* Weapon already fully loaded with the same ammunition -> abort */ return IA_NORELOAD; } time += ic->def()->getReloadTime(); if (!TU || *TU >= time) { if (TU) *TU -= time; if (ic->getAmmoLeft() >= ic->def()->ammo) { /* exchange ammo */ const Item item(ic->ammoDef()); /* Put current ammo in place of the new ammo unless floor - there can be more than 1 item */ const int cacheFromX = from->isFloorDef() ? NONE : fItem->getX(); const int cacheFromY = from->isFloorDef() ? NONE : fItem->getY(); /* Actually remove the ammo from the 'from' container. */ if (!removeFromInventory(inv, from, fItem)) return IA_NONE; /* Add the currently used ammo in place of the new ammo in the "from" container. */ if (addToInventory(inv, &item, from, cacheFromX, cacheFromY, 1) == nullptr) Sys_Error("Could not reload the weapon - add to inventory failed (%s)", invName); ic->setAmmoDef(this->cacheItem.def()); if (uponItem) *uponItem = ic; return IA_RELOAD_SWAP; } else { /* Actually remove the ammo from the 'from' container. */ if (!removeFromInventory(inv, from, fItem)) return IA_NONE; ic->setAmmoDef(this->cacheItem.def()); /* loose ammo of type ic->m saved on server side */ ic->setAmmoLeft(ic->def()->ammo); if (uponItem) *uponItem = ic; return IA_RELOAD; } } /* Not enough time -> abort. */ return IA_NOTIME; } /* temp container like CID_EQUIP and CID_FLOOR */ 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 addToInventory below.*/ /** @todo change the other code to browse through these things. */ inv->findSpace(to, fItem, &tx, &ty, fItem); if (tx == NONE || ty == NONE) { Com_DPrintf(DEBUG_SHARED, "MoveInInventory - item will be added non-visible (%s)\n", invName); } } else { /* Impossible move -> abort. */ return IA_NONE; } } /* twohanded exception - only CID_RIGHT is allowed for fireTwoHanded weapons */ if (fItem->def()->fireTwoHanded && to->isLeftDef()) to = &this->csi->ids[CID_RIGHT]; switch (checkedTo) { case INV_DOES_NOT_FIT: /* Impossible move - should be handled above, but add an abort just in case */ Com_Printf("MoveInInventory: Item doesn't fit into container."); return IA_NONE; case INV_FITS: /* Remove rotated tag */ fItem->rotated = false; break; case INV_FITS_ONLY_ROTATED: /* Set rotated tag */ fItem->rotated = true; break; case INV_FITS_BOTH: /* Leave rotated tag as-is */ break; } /* Actually remove the item from the 'from' container (if it wasn't already removed). */ if (!alreadyRemovedSource) if (!removeFromInventory(inv, from, fItem)) return IA_NONE; /* successful */ if (TU) *TU -= time; assert(this->cacheItem.def()); ic = addToInventory(inv, &this->cacheItem, to, tx, ty, 1); /* return data */ if (uponItem) { assert(ic); *uponItem = ic; } if (to->isArmourDef()) { assert(this->cacheItem.isArmour()); return IA_ARMOUR; } return IA_MOVE; }
/** * @param[in] inv The inventory the container is in. * @param[in] container The container where the item should be removed. * @param[in] fItem The item to be removed. * @return true If removal was successful. * @return false If nothing was removed or an error occurred. * @sa addToInventory */ bool InventoryInterface::removeFromInventory (Inventory* const inv, const invDef_t* container, Item* fItem) { assert(inv); assert(container); assert(fItem); Item* ic = inv->getContainer2(container->id); if (!ic) return false; /** @todo the problem here is, that in case of a move inside the same container * the item don't just get updated x and y values but it is tried to remove * one of the items => crap - maybe we have to change the inventory move function * to check for this case of move and only update the x and y coordinates instead * of calling the add and remove functions */ if (container->single || ic == fItem) { this->cacheItem = *ic; /* temp container like CID_EQUIP and CID_FLOOR */ if (container->temp && ic->getAmount() > 1) { ic->addAmount(-1); Com_DPrintf(DEBUG_SHARED, "removeFromInventory: Amount of '%s': %i (%s)\n", ic->def()->name, ic->getAmount(), invName); return true; } if (container->single && ic->getNext()) Com_Printf("removeFromInventory: Error: single container %s has many items. (%s)\n", container->name, invName); /* An item in other containers than CID_FLOOR or CID_EQUIP should * always have an amount value of 1. * The other container types do not support stacking.*/ assert(ic->getAmount() == 1); inv->setContainer(container->id, ic->getNext()); /* updated invUnused to be able to reuse this space later again */ removeInvList(ic); return true; } for (Item* previous = inv->getContainer2(container->id); ic; ic = ic->getNext()) { if (ic != fItem) { previous = ic; continue; } this->cacheItem = *ic; /* temp container like CID_EQUIP and CID_FLOOR */ if (ic->getAmount() > 1 && container->temp) { ic->addAmount(-1); Com_DPrintf(DEBUG_SHARED, "removeFromInventory: Amount of '%s': %i (%s)\n", ic->def()->name, ic->getAmount(), invName); return true; } if (ic == inv->getContainer2(container->id)) inv->setContainer(container->id, inv->getContainer2(container->id)->getNext()); else previous->setNext(ic->getNext()); removeInvList(ic); return true; } return false; }