inv_shared.c

Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 2002-2010 UFO: Alien Invasion.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 #include "q_shared.h"
00029 
00030 static csi_t *CSI;
00031 
00039 void INVSH_InitCSI (csi_t * import)
00040 {
00041     CSI = import;
00042 }
00043 
00049 qboolean INV_IsFloorDef (const invDef_t* invDef)
00050 {
00051     return invDef->id == CSI->idFloor;
00052 }
00053 
00059 qboolean INV_IsRightDef (const invDef_t* invDef)
00060 {
00061     return invDef->id == CSI->idRight;
00062 }
00063 
00069 qboolean INV_IsLeftDef (const invDef_t* invDef)
00070 {
00071     return invDef->id == CSI->idLeft;
00072 }
00073 
00079 qboolean INV_IsEquipDef (const invDef_t* invDef)
00080 {
00081     return invDef->id == CSI->idEquip;
00082 }
00083 
00089 qboolean INV_IsArmourDef (const invDef_t* invDef)
00090 {
00091     return invDef->id == CSI->idArmour;
00092 }
00093 
00094 invList_t* INVSH_HasArmour (const inventory_t *inv)
00095 {
00096     return inv->c[CSI->idArmour];
00097 }
00098 
00099 static int cacheCheckToInventory = INV_DOES_NOT_FIT;
00100 
00109 static qboolean INVSH_CheckShapeCollision (const uint32_t *shape, const uint32_t itemShape, const int x, const int y)
00110 {
00111     int i;
00112 
00113     /* Negative positions not allowed (all items are supposed to have at least one bit set in the first row and column) */
00114     if (x < 0 || y < 0) {
00115         Com_DPrintf(DEBUG_SHARED, "INVSH_CheckShapeCollision: x or y value negative: x=%i y=%i!\n", x, y);
00116         return qtrue;
00117     }
00118 
00119     for (i = 0; i < SHAPE_SMALL_MAX_HEIGHT; i++) {
00120         /* 0xFF is the length of one row in a "small shape" i.e. SHAPE_SMALL_MAX_WIDTH */
00121         const int itemRow = (itemShape >> (i * SHAPE_SMALL_MAX_WIDTH)) & 0xFF;
00122         /* Result has to be limited to 32bit (SHAPE_BIG_MAX_WIDTH) */
00123         const uint32_t itemRowShifted = itemRow << x;
00124 
00125         /* Check x maximum. */
00126         if (itemRowShifted >> x != itemRow)
00127             /* Out of bounds (32bit; a few bits of this row in itemShape were truncated) */
00128             return qtrue;
00129 
00130         /* Check y maximum. */
00131         if (y + i >= SHAPE_BIG_MAX_HEIGHT && itemRow)
00132             /* This row (row "i" in itemShape) is outside of the max. bound and has bits in it. */
00133             return qtrue;
00134 
00135         /* Check for collisions of the item with the big mask. */
00136         if (itemRowShifted & shape[y + i])
00137             return qtrue;
00138     }
00139 
00140     return qfalse;
00141 }
00142 
00154 static qboolean INVSH_CheckToInventory_shape (const inventory_t * const i, const invDef_t * container, const uint32_t itemShape, const int x, const int y, const invList_t *ignoredItem)
00155 {
00156     invList_t *ic;
00157     static uint32_t mask[SHAPE_BIG_MAX_HEIGHT];
00158 
00159     assert(container);
00160 
00161     if (container->scroll)
00162         Sys_Error("INVSH_CheckToInventory_shape: No scrollable container will ever use this. This type does not support grid-packing!");
00163 
00164     /* check bounds */
00165     if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH || y >= SHAPE_BIG_MAX_HEIGHT)
00166         return qfalse;
00167 
00168     if (!cacheCheckToInventory) {
00169         int j;
00170         /* extract shape info */
00171         for (j = 0; j < SHAPE_BIG_MAX_HEIGHT; j++)
00172             mask[j] = ~container->shape[j];
00173 
00174         /* Add other items to mask. (i.e. merge their shapes at their location into the generated mask) */
00175         for (ic = i->c[container->id]; ic; ic = ic->next) {
00176             if (ignoredItem == ic)
00177                 continue;
00178 
00179             if (ic->item.rotated)
00180                 INVSH_MergeShapes(mask, INVSH_ShapeRotate(ic->item.t->shape), ic->x, ic->y);
00181             else
00182                 INVSH_MergeShapes(mask, ic->item.t->shape, ic->x, ic->y);
00183         }
00184     }
00185 
00186     /* Test for collisions with newly generated mask. */
00187     if (INVSH_CheckShapeCollision(mask, itemShape, x, y))
00188         return qfalse;
00189 
00190     /* Everything ok. */
00191     return qtrue;
00192 }
00193 
00206 int INVSH_CheckToInventory (const inventory_t * const i, const objDef_t *od, const invDef_t * container, const int x, const int y, const invList_t *ignoredItem)
00207 {
00208     int fits;
00209     assert(i);
00210     assert(container);
00211     assert(od);
00212 
00213     /* armour vs item */
00214     if (INV_IsArmour(od)) {
00215         if (!container->armour && !container->all) {
00216             return INV_DOES_NOT_FIT;
00217         }
00218     } else if (!od->extension && container->extension) {
00219         return INV_DOES_NOT_FIT;
00220     } else if (!od->headgear && container->headgear) {
00221         return INV_DOES_NOT_FIT;
00222     } else if (container->armour) {
00223         return INV_DOES_NOT_FIT;
00224     }
00225 
00226     /* twohanded item */
00227     if (od->holdTwoHanded) {
00228         if ((INV_IsRightDef(container) && i->c[CSI->idLeft]) || INV_IsLeftDef(container))
00229             return INV_DOES_NOT_FIT;
00230     }
00231 
00232     /* left hand is busy if right wields twohanded */
00233     if (INV_IsLeftDef(container)) {
00234         if (i->c[CSI->idRight] && i->c[CSI->idRight]->item.t->holdTwoHanded)
00235             return INV_DOES_NOT_FIT;
00236 
00237         /* can't put an item that is 'fireTwoHanded' into the left hand */
00238         if (od->fireTwoHanded)
00239             return INV_DOES_NOT_FIT;
00240     }
00241 
00242     /* Single item containers, e.g. hands, extension or headgear. */
00243     if (container->single) {
00244         if (i->c[container->id]) {
00245             /* There is already an item. */
00246             return INV_DOES_NOT_FIT;
00247         } else {
00248             fits = INV_DOES_NOT_FIT; /* equals 0 */
00249 
00250             if (INVSH_CheckToInventory_shape(i, container, od->shape, x, y, ignoredItem))
00251                 fits |= INV_FITS;
00252             if (INVSH_CheckToInventory_shape(i, container, INVSH_ShapeRotate(od->shape), x, y, ignoredItem))
00253                 fits |= INV_FITS_ONLY_ROTATED;
00254 
00255             if (fits != INV_DOES_NOT_FIT)
00256                 return fits;    
00258             Com_DPrintf(DEBUG_SHARED, "INVSH_CheckToInventory: INFO: Moving to 'single' container but item would not fit normally.\n");
00259             return INV_FITS; 
00260         }
00261     }
00262 
00263     /* Scrolling container have endless room, the item always fits. */
00264     if (container->scroll)
00265         return INV_FITS;
00266 
00267     /* Check 'grid' containers. */
00268     fits = INV_DOES_NOT_FIT; /* equals 0 */
00269     if (INVSH_CheckToInventory_shape(i, container, od->shape, x, y, ignoredItem))
00270         fits |= INV_FITS;
00272     if (!INV_IsEquipDef(container) && !INV_IsFloorDef(container)
00273     && INVSH_CheckToInventory_shape(i, container, INVSH_ShapeRotate(od->shape), x, y, ignoredItem))
00274         fits |= INV_FITS_ONLY_ROTATED;
00275 
00276     return fits;    
00277 }
00278 
00285 qboolean INVSH_CompareItem (item_t *item1, item_t *item2)
00286 {
00287     if (item1->t == item2->t && item1->m == item2->m && item1->a == item2->a)
00288         return qtrue;
00289 
00290     return qfalse;
00291 }
00292 
00299 static qboolean INVSH_ShapeCheckPosition (const invList_t *ic, const int x, const int y)
00300 {
00301     assert(ic);
00302 
00303     /* Check if the position is inside the shape (depending on rotation value) of the item. */
00304     if (ic->item.rotated) {
00305         if (((INVSH_ShapeRotate(ic->item.t->shape) >> (x - ic->x) >> (y - ic->y) * SHAPE_SMALL_MAX_WIDTH)) & 1)
00306             return qtrue;
00307     } else {
00308         if (((ic->item.t->shape >> (x - ic->x) >> (y - ic->y) * SHAPE_SMALL_MAX_WIDTH)) & 1)
00309             return qtrue;
00310     }
00311 
00312     /* Position is out of bounds or position not inside item-shape. */
00313     return qfalse;
00314 }
00315 
00324 void INVSH_GetFirstShapePosition (const invList_t *ic, int* const x, int* const y)
00325 {
00326     int tempX, tempY;
00327 
00328     assert(ic);
00329 
00330     for (tempX = 0; tempX < SHAPE_SMALL_MAX_HEIGHT; tempX++)
00331         for (tempY = 0; tempY < SHAPE_SMALL_MAX_HEIGHT; tempY++)
00332             if (INVSH_ShapeCheckPosition(ic, ic->x + tempX, ic->y + tempY)) {
00333                 *x = tempX;
00334                 *y = tempY;
00335                 return;
00336             }
00337 
00338     *x = *y = NONE;
00339 }
00340 
00348 qboolean INVSH_ExistsInInventory (const inventory_t* const inv, const invDef_t * container, item_t item)
00349 {
00350     invList_t *ic;
00351 
00352     for (ic = inv->c[container->id]; ic; ic = ic->next)
00353         if (INVSH_CompareItem(&ic->item, &item)) {
00354             return qtrue;
00355         }
00356 
00357     return qfalse;
00358 }
00359 
00369 qboolean INV_IsCraftItem (const objDef_t *obj)
00370 {
00371     return obj->craftitem.type != MAX_ACITEMS && !obj->isDummy;
00372 }
00373 
00383 qboolean INV_IsBaseDefenceItem (const objDef_t *obj)
00384 {
00385     return obj->craftitem.type != MAX_ACITEMS && obj->isDummy;
00386 }
00387 
00396 invList_t *INVSH_SearchInInventory (const inventory_t* const i, const invDef_t * container, const int x, const int y)
00397 {
00398     invList_t *ic;
00399 
00400     assert(container);
00401 
00402     /* Only a single item. */
00403     if (container->single)
00404         return i->c[container->id];
00405 
00406     if (container->scroll)
00407         Sys_Error("INVSH_SearchInInventory: Scrollable containers (%i:%s) are not supported by this function.\nUse INV_SearchInScrollableContainer instead!",
00408                 container->id, container->name);
00409 
00410     /* More than one item - search for the item that is located at location x/y in this container. */
00411     for (ic = i->c[container->id]; ic; ic = ic->next)
00412         if (INVSH_ShapeCheckPosition(ic, x, y))
00413             return ic;
00414 
00415     /* Found nothing. */
00416     return NULL;
00417 }
00418 
00430 void INVSH_FindSpace (const inventory_t* const inv, const item_t *item, const invDef_t * container, int* const px, int* const py, const invList_t *ignoredItem)
00431 {
00432     int x, y;
00433 
00434     assert(inv);
00435     assert(container);
00436     assert(!cacheCheckToInventory);
00437 
00438     /* Scrollable container always have room. We return a dummy location. */
00439     if (container->scroll) {
00440         *px = *py = 0;
00441         return;
00442     }
00443 
00446     for (y = 0; y < SHAPE_BIG_MAX_HEIGHT; y++) {
00447         for (x = 0; x < SHAPE_BIG_MAX_WIDTH; x++) {
00448             const int checkedTo = INVSH_CheckToInventory(inv, item->t, container, x, y, ignoredItem);
00449             if (checkedTo) {
00450                 cacheCheckToInventory = INV_DOES_NOT_FIT;
00451                 *px = x;
00452                 *py = y;
00453                 return;
00454             } else {
00455                 cacheCheckToInventory = INV_FITS;
00456             }
00457         }
00458     }
00459     cacheCheckToInventory = INV_DOES_NOT_FIT;
00460 
00461 #ifdef PARANOID
00462     Com_DPrintf(DEBUG_SHARED, "INVSH_FindSpace: no space for %s: %s in %s\n",
00463         item->t->type, item->t->id, container->name);
00464 #endif
00465     *px = *py = NONE;
00466 }
00467 
00475 objDef_t *INVSH_GetItemByIDSilent (const char *id)
00476 {
00477     int i;
00478 
00479     if (!id)
00480         return NULL;
00481     for (i = 0; i < CSI->numODs; i++) { /* i = item index */
00482         objDef_t *item = &CSI->ods[i];
00483         if (!strcmp(id, item->id)) {
00484             return item;
00485         }
00486     }
00487     return NULL;
00488 }
00489 
00493 objDef_t *INVSH_GetItemByIDX (int index)
00494 {
00495     if (index == NONE)
00496         return NULL;
00497 
00498     if (index < 0 || index >= CSI->numODs)
00499         Sys_Error("Invalid object index given: %i", index);
00500 
00501     return &CSI->ods[index];
00502 }
00503 
00509 objDef_t *INVSH_GetItemByID (const char *id)
00510 {
00511     objDef_t *od = INVSH_GetItemByIDSilent(id);
00512     if (!od)
00513         Com_Printf("INVSH_GetItemByID: Item \"%s\" not found.\n", id);
00514 
00515     return od;
00516 }
00517 
00523 invDef_t *INVSH_GetInventoryDefinitionByID (const char *id)
00524 {
00525     containerIndex_t i;
00526     invDef_t *container;
00527 
00528     for (i = 0, container = CSI->ids; i < CSI->numIDs; container++, i++)
00529         if (!strcmp(id, container->name))
00530             return container;
00531 
00532     return NULL;
00533 }
00534 
00541 qboolean INVSH_LoadableInWeapon (const objDef_t *od, const objDef_t *weapon)
00542 {
00543     int i;
00544     qboolean usable = qfalse;
00545 
00546 #ifdef DEBUG
00547     if (!od) {
00548         Com_DPrintf(DEBUG_SHARED, "INVSH_LoadableInWeapon: No pointer given for 'od'.\n");
00549         return qfalse;
00550     }
00551     if (!weapon) {
00552         Com_DPrintf(DEBUG_SHARED, "INVSH_LoadableInWeapon: No weapon pointer given.\n");
00553         return qfalse;
00554     }
00555 #endif
00556 
00557     if (od && od->numWeapons == 1 && od->weapons[0] && od->weapons[0] == od) {
00558         /* The weapon is only linked to itself. */
00559         return qfalse;
00560     }
00561 
00562     for (i = 0; i < od->numWeapons; i++) {
00563 #ifdef DEBUG
00564         if (!od->weapons[i]) {
00565             Com_DPrintf(DEBUG_SHARED, "INVSH_LoadableInWeapon: No weapon pointer set for the %i. entry found in item '%s'.\n", i, od->id);
00566             break;
00567         }
00568 #endif
00569         if (weapon == od->weapons[i]) {
00570             usable = qtrue;
00571             break;
00572         }
00573     }
00574 
00575     return usable;
00576 }
00577 
00578 /*
00579 ===============================
00580 FIREMODE MANAGEMENT FUNCTIONS
00581 ===============================
00582 */
00583 
00592 const fireDef_t* FIRESH_GetFiredef (const objDef_t *obj, const weaponFireDefIndex_t weapFdsIdx, const fireDefIndex_t fdIdx)
00593 {
00594     if (weapFdsIdx < 0 || weapFdsIdx >= MAX_WEAPONS_PER_OBJDEF)
00595         Sys_Error("FIRESH_GetFiredef: weapFdsIdx out of bounds [%i] for item '%s'", weapFdsIdx, obj->id);
00596     if (fdIdx < 0 || fdIdx >= MAX_FIREDEFS_PER_WEAPON)
00597         Sys_Error("FIRESH_GetFiredef: fdIdx out of bounds [%i] for item '%s'", fdIdx, obj->id);
00598     return &obj->fd[weapFdsIdx & (MAX_WEAPONS_PER_OBJDEF - 1)][fdIdx & (MAX_FIREDEFS_PER_WEAPON - 1)];
00599 }
00600 
00607 const fireDef_t *FIRESH_FiredefForWeapon (const item_t *item)
00608 {
00609     int i;
00610     const objDef_t *ammo = item->m;
00611     const objDef_t *weapon = item->t;
00612 
00613     /* this weapon does not use ammo, check for
00614      * existing firedefs in the weapon. */
00615     if (weapon->numWeapons > 0)
00616         ammo = item->t;
00617 
00618     if (!ammo)
00619         return NULL;
00620 
00621     for (i = 0; i < ammo->numWeapons; i++) {
00622         if (weapon == ammo->weapons[i])
00623             return &ammo->fd[i][0];
00624     }
00625 
00626     return NULL;
00627 }
00628 
00634 const objDef_t* INVSH_HasReactionFireEnabledWeapon (const invList_t *invList)
00635 {
00636     if (!invList)
00637         return NULL;
00638 
00639     while (invList) {
00640         if (invList->item.t) {
00641             const fireDef_t *fd = FIRESH_FiredefForWeapon(&invList->item);
00642             if (fd && fd->reaction)
00643                 return invList->item.t;
00644         }
00645         invList = invList->next;
00646     }
00647 
00648     return NULL;
00649 }
00650 
00659 void INVSH_MergeShapes (uint32_t *shape, const uint32_t itemShape, const int x, const int y)
00660 {
00661     int i;
00662 
00663     for (i = 0; (i < SHAPE_SMALL_MAX_HEIGHT) && (y + i < SHAPE_BIG_MAX_HEIGHT); i++)
00664         shape[y + i] |= ((itemShape >> i * SHAPE_SMALL_MAX_WIDTH) & 0xFF) << x;
00665 }
00666 
00673 qboolean INVSH_CheckShape (const uint32_t *shape, const int x, const int y)
00674 {
00675     const uint32_t row = shape[y];
00676     int position = pow(2, x);
00677 
00678     if (y >= SHAPE_BIG_MAX_HEIGHT || x >= SHAPE_BIG_MAX_WIDTH || x < 0 || y < 0) {
00679         Com_Printf("INVSH_CheckShape: Bad x or y value: (x=%i, y=%i)\n", x, y);
00680         return qfalse;
00681     }
00682 
00683     if ((row & position) == 0)
00684         return qfalse;
00685     else
00686         return qtrue;
00687 }
00688 
00695 static qboolean INVSH_CheckShapeSmall (const uint32_t shape, const int x, const int y)
00696 {
00697     if (y >= SHAPE_BIG_MAX_HEIGHT || x >= SHAPE_BIG_MAX_WIDTH || x < 0 || y < 0) {
00698         Com_Printf("INVSH_CheckShapeSmall: Bad x or y value: (x=%i, y=%i)\n", x, y);
00699         return qfalse;
00700     }
00701 
00702     return shape & (0x01 << (y * SHAPE_SMALL_MAX_WIDTH + x));
00703 }
00704 
00711 int INVSH_ShapeSize (const uint32_t shape)
00712 {
00713     int bitCounter = 0;
00714     int i;
00715 
00716     for (i = 0; i < SHAPE_SMALL_MAX_HEIGHT * SHAPE_SMALL_MAX_WIDTH; i++)
00717         if (shape & (1 << i))
00718             bitCounter++;
00719 
00720     return bitCounter;
00721 }
00722 
00732 static uint32_t INVSH_ShapeSetBit (uint32_t shape, const int x, const int y)
00733 {
00734     if (x >= SHAPE_SMALL_MAX_WIDTH || y >= SHAPE_SMALL_MAX_HEIGHT || x < 0 || y < 0) {
00735         Com_Printf("INVSH_ShapeSetBit: Bad x or y value: (x=%i, y=%i)\n", x,y);
00736         return shape;
00737     }
00738 
00739     shape |= 0x01 << (y * SHAPE_SMALL_MAX_WIDTH + x);
00740     return shape;
00741 }
00742 
00743 
00750 uint32_t INVSH_ShapeRotate (const uint32_t shape)
00751 {
00752     int h, w;
00753     uint32_t shapeNew = 0;
00754     int maxWidth = -1;
00755 
00756     for (w = SHAPE_SMALL_MAX_WIDTH - 1; w >= 0; w--) {
00757         for (h = 0; h < SHAPE_SMALL_MAX_HEIGHT; h++) {
00758             if (INVSH_CheckShapeSmall(shape, w, h)) {
00759                 if (w >= SHAPE_SMALL_MAX_HEIGHT) {
00760                     /* Object can't be rotated (code-wise), it is longer than SHAPE_SMALL_MAX_HEIGHT allows. */
00761                     return shape;
00762                 }
00763 
00764                 if (maxWidth < 0)
00765                     maxWidth = w;
00766 
00767                 shapeNew = INVSH_ShapeSetBit(shapeNew, h, maxWidth - w);
00768             }
00769         }
00770     }
00771 
00772     return shapeNew;
00773 }

Generated by  doxygen 1.6.2