inventory.c

Go to the documentation of this file.
00001 #include "inventory.h"
00002 
00003 static inline void I_Free (inventoryInterface_t* self, void *data)
00004 {
00005     self->import->Free(data);
00006 }
00007 
00008 static inline void *I_Alloc (inventoryInterface_t* self, size_t size)
00009 {
00010     return self->import->Alloc(size);
00011 }
00012 
00013 static void I_RemoveInvList (inventoryInterface_t* self, invList_t *invList)
00014 {
00015     Com_DPrintf(DEBUG_SHARED, "I_RemoveInvList: remove one slot (%s)\n", self->name);
00016 
00017     /* first entry */
00018     if (self->invList == invList) {
00019         invList_t *ic = self->invList;
00020         self->invList = ic->next;
00021         I_Free(self, ic);
00022     } else {
00023         invList_t *ic = self->invList;
00024         invList_t* prev = NULL;
00025         while (ic) {
00026             if (ic == invList) {
00027                 prev->next = ic->next;
00028                 I_Free(self, ic);
00029                 break;
00030             }
00031             prev = ic;
00032             ic = ic->next;
00033         }
00034     }
00035 }
00036 
00037 static invList_t* I_AddInvList (inventoryInterface_t* self, invList_t **invList)
00038 {
00039     invList_t *newEntry;
00040     invList_t *list;
00041 
00042     Com_DPrintf(DEBUG_SHARED, "I_AddInvList: add one slot (%s)\n", self->name);
00043 
00044     /* create the list */
00045     if (!*invList) {
00046         *invList = (invList_t*)I_Alloc(self, sizeof(**invList));
00047         (*invList)->next = NULL; /* not really needed - but for better readability */
00048         return *invList;
00049     } else
00050         list = *invList;
00051 
00052     while (list->next)
00053         list = list->next;
00054 
00055     newEntry = (invList_t*)I_Alloc(self, sizeof(*newEntry));
00056     list->next = newEntry;
00057     newEntry->next = NULL; /* not really needed - but for better readability */
00058 
00059     return newEntry;
00060 }
00061 
00074 static invList_t *I_AddToInventory (inventoryInterface_t* self, inventory_t * const i, item_t item, const invDef_t * container, int x, int y, int amount)
00075 {
00076     invList_t *ic;
00077 
00078     if (!item.t)
00079         return NULL;
00080 
00081     if (amount <= 0)
00082         return NULL;
00083 
00084     assert(i);
00085     assert(container);
00086 
00087     if (container->single && i->c[container->id] && i->c[container->id]->next)
00088         return NULL;
00089 
00114     /* idEquip and idFloor */
00115     if (container->temp) {
00116         for (ic = i->c[container->id]; ic; ic = ic->next)
00117             if (INVSH_CompareItem(&ic->item, &item)) {
00118                 ic->item.amount += amount;
00119                 Com_DPrintf(DEBUG_SHARED, "I_AddToInventory: Amount of '%s': %i (%s)\n",
00120                     ic->item.t->name, ic->item.amount, self->name);
00121                 return ic;
00122             }
00123     }
00124 
00125     if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH || y >= SHAPE_BIG_MAX_HEIGHT) {
00126         /* No (sane) position in container given as parameter - find free space on our own. */
00127         INVSH_FindSpace(i, &item, container, &x, &y, NULL);
00128         if (x == NONE)
00129             return NULL;
00130     }
00131 
00132     /* not found - add a new one */
00133     ic = I_AddInvList(self, &i->c[container->id]);
00134 
00135     /* Set the data in the new entry to the data we got via function-parameters.*/
00136     ic->item = item;
00137     ic->item.amount = amount;
00138     ic->x = x;
00139     ic->y = y;
00140 
00141     return ic;
00142 }
00143 
00152 static qboolean I_RemoveFromInventory (inventoryInterface_t* self, inventory_t* const i, const invDef_t * container, invList_t *fItem)
00153 {
00154     invList_t *ic, *previous;
00155 
00156     assert(i);
00157     assert(container);
00158     assert(fItem);
00159 
00160     ic = i->c[container->id];
00161     if (!ic)
00162         return qfalse;
00163 
00169     if (container->single || ic == fItem) {
00170         self->cacheItem = ic->item;
00171         /* temp container like idEquip and idFloor */
00172         if (container->temp && ic->item.amount > 1) {
00173             ic->item.amount--;
00174             Com_DPrintf(DEBUG_SHARED, "I_RemoveFromInventory: Amount of '%s': %i (%s)\n",
00175                 ic->item.t->name, ic->item.amount, self->name);
00176             return qtrue;
00177         }
00178 
00179         if (container->single && ic->next)
00180             Com_Printf("I_RemoveFromInventory: Error: single container %s has many items. (%s)\n", container->name, self->name);
00181 
00182         /* An item in other containers than idFloor or idEquip should
00183          * always have an amount value of 1.
00184          * The other container types do not support stacking.*/
00185         assert(ic->item.amount == 1);
00186 
00187         i->c[container->id] = ic->next;
00188 
00189         /* updated invUnused to be able to reuse this space later again */
00190         I_RemoveInvList(self, ic);
00191 
00192         return qtrue;
00193     }
00194 
00195     for (previous = i->c[container->id]; ic; ic = ic->next) {
00196         if (ic == fItem) {
00197             self->cacheItem = ic->item;
00198             /* temp container like idEquip and idFloor */
00199             if (ic->item.amount > 1 && container->temp) {
00200                 ic->item.amount--;
00201                 Com_DPrintf(DEBUG_SHARED, "I_RemoveFromInventory: Amount of '%s': %i (%s)\n",
00202                     ic->item.t->name, ic->item.amount, self->name);
00203                 return qtrue;
00204             }
00205 
00206             if (ic == i->c[container->id])
00207                 i->c[container->id] = i->c[container->id]->next;
00208             else
00209                 previous->next = ic->next;
00210 
00211             I_RemoveInvList(self, ic);
00212 
00213             return qtrue;
00214         }
00215         previous = ic;
00216     }
00217     return qfalse;
00218 }
00219 
00239 static int I_MoveInInventory (inventoryInterface_t* self, inventory_t* const i, const invDef_t * from, invList_t *fItem, const invDef_t * to, int tx, int ty, int *TU, invList_t ** icp)
00240 {
00241     invList_t *ic;
00242 
00243     int time;
00244     int checkedTo = INV_DOES_NOT_FIT;
00245     qboolean alreadyRemovedSource = qfalse;
00246 
00247     assert(to);
00248     assert(from);
00249 
00250     if (icp)
00251         *icp = NULL;
00252 
00253     if (from == to && fItem->x == tx && fItem->y == ty)
00254         return IA_NONE;
00255 
00256     time = from->out + to->in;
00257     if (from == to) {
00258         if (INV_IsFloorDef(from))
00259             time = 0;
00260         else
00261             time /= 2;
00262     }
00263 
00264     if (TU && *TU < time)
00265         return IA_NOTIME;
00266 
00267     assert(i);
00268 
00269     /* Special case for moving an item within the same container. */
00270     if (from == to) {
00271         /* Do nothing if we move inside a scroll container. */
00272         if (from->scroll)
00273             return IA_NONE;
00274 
00275         ic = i->c[from->id];
00276         for (; ic; ic = ic->next) {
00277             if (ic == fItem) {
00278                 if (ic->item.amount > 1) {
00279                     checkedTo = INVSH_CheckToInventory(i, ic->item.t, to, tx, ty, fItem);
00280                     if (checkedTo & INV_FITS) {
00281                         ic->x = tx;
00282                         ic->y = ty;
00283                         if (icp)
00284                             *icp = ic;
00285                         return IA_MOVE;
00286                     }
00287                     return IA_NONE;
00288                 }
00289             }
00290         }
00291     }
00292 
00293     /* If weapon is twohanded and is moved from hand to hand do nothing. */
00294     /* Twohanded weapon are only in CSI->idRight. */
00295     if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to) && INV_IsRightDef(from)) {
00296         return IA_NONE;
00297     }
00298 
00299     /* If non-armour moved to an armour slot then abort.
00300      * Same for non extension items when moved to an extension slot. */
00301     if ((to->armour && !INV_IsArmour(fItem->item.t))
00302      || (to->extension && !fItem->item.t->extension)
00303      || (to->headgear && !fItem->item.t->headgear)) {
00304         return IA_NONE;
00305     }
00306 
00307     /* Check if the target is a blocked inv-armour and source!=dest. */
00308     if (to->single)
00309         checkedTo = INVSH_CheckToInventory(i, fItem->item.t, to, 0, 0, fItem);
00310     else {
00311         if (tx == NONE || ty == NONE)
00312             INVSH_FindSpace(i, &fItem->item, to, &tx, &ty, fItem);
00313         /* still no valid location found */
00314         if (tx == NONE || ty == NONE)
00315             return IA_NONE;
00316 
00317         checkedTo = INVSH_CheckToInventory(i, fItem->item.t, to, tx, ty, fItem);
00318     }
00319 
00320     if (to->armour && from != to && !checkedTo) {
00321         item_t cacheItem2;
00322         invList_t *icTo;
00323         /* Store x/y origin coordinates of removed (source) item.
00324          * When we re-add it we can use this. */
00325         const int cacheFromX = fItem->x;
00326         const int cacheFromY = fItem->y;
00327 
00328         /* Check if destination/blocking item is the same as source/from item.
00329          * In that case the move is not needed -> abort. */
00330         icTo = INVSH_SearchInInventory(i, to, tx, ty);
00331         if (fItem->item.t == icTo->item.t)
00332             return IA_NONE;
00333 
00334         /* Actually remove the ammo from the 'from' container. */
00335         if (!self->RemoveFromInventory(self, i, from, fItem))
00336             return IA_NONE;
00337         else
00338             /* Removal successful - store this info. */
00339             alreadyRemovedSource = qtrue;
00340 
00341         cacheItem2 = self->cacheItem; /* Save/cache (source) item. The cacheItem is modified in I_MoveInInventory. */
00342 
00343         /* Move the destination item to the source. */
00344         self->MoveInInventory(self, i, to, icTo, from, cacheFromX, cacheFromY, TU, icp);
00345 
00346         /* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */
00347         self->cacheItem = cacheItem2;
00348     } else if (!checkedTo) {
00349         /* Get the target-invlist (e.g. a weapon). We don't need to check for
00350          * scroll because checkedTo is always true here. */
00351         ic = INVSH_SearchInInventory(i, to, tx, ty);
00352 
00353         if (ic && !INV_IsEquipDef(to) && INVSH_LoadableInWeapon(fItem->item.t, ic->item.t)) {
00354             /* A target-item was found and the dragged item (implicitly ammo)
00355              * can be loaded in it (implicitly weapon). */
00356             if (ic->item.a >= ic->item.t->ammo && ic->item.m == fItem->item.t) {
00357                 /* Weapon already fully loaded with the same ammunition -> abort */
00358                 return IA_NORELOAD;
00359             }
00360             time += ic->item.t->reload;
00361             if (!TU || *TU >= time) {
00362                 if (TU)
00363                     *TU -= time;
00364                 if (ic->item.a >= ic->item.t->ammo) {
00365                     /* exchange ammo */
00366                     const item_t item = {NONE_AMMO, NULL, ic->item.m, 0, 0};
00367 
00368                     /* Actually remove the ammo from the 'from' container. */
00369                     if (!self->RemoveFromInventory(self, i, from, fItem))
00370                         return IA_NONE;
00371 
00372                     /* Add the currently used ammo in a free place of the "from" container. */
00373                     if (self->AddToInventory(self, i, item, from, NONE, NONE, 1) == NULL)
00374                         Sys_Error("Could not reload the weapon - add to inventory failed (%s)", self->name);
00375 
00376                     ic->item.m = self->cacheItem.t;
00377                     if (icp)
00378                         *icp = ic;
00379                     return IA_RELOAD_SWAP;
00380                 } else {
00381                     /* Actually remove the ammo from the 'from' container. */
00382                     if (!self->RemoveFromInventory(self, i, from, fItem))
00383                         return IA_NONE;
00384 
00385                     ic->item.m = self->cacheItem.t;
00386                     /* loose ammo of type ic->item.m saved on server side */
00387                     ic->item.a = ic->item.t->ammo;
00388                     if (icp)
00389                         *icp = ic;
00390                     return IA_RELOAD;
00391                 }
00392             }
00393             /* Not enough time -> abort. */
00394             return IA_NOTIME;
00395         }
00396 
00397         /* temp container like idEquip and idFloor */
00398         if (ic && to->temp) {
00399             /* We are moving to a blocked location container but it's the base-equipment floor or a battlescape floor.
00400              * We add the item anyway but it'll not be displayed (yet)
00401              * This is then used in I_AddToInventory below.*/
00403             INVSH_FindSpace(i, &fItem->item, to, &tx, &ty, fItem);
00404             if (tx == NONE || ty == NONE) {
00405                 Com_DPrintf(DEBUG_SHARED, "I_MoveInInventory - item will be added non-visible (%s)\n", self->name);
00406             }
00407         } else {
00408             /* Impossible move -> abort. */
00409             return IA_NONE;
00410         }
00411     }
00412 
00413     /* twohanded exception - only CSI->idRight is allowed for fireTwoHanded weapons */
00414     if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to))
00415         to = &self->csi->ids[self->csi->idRight];
00416 
00417     if (checkedTo == INV_FITS_ONLY_ROTATED) {
00418         /* Set rotated tag */
00419         fItem->item.rotated = qtrue;
00420     } else if (fItem->item.rotated) {
00421         /* Remove rotated tag */
00422         fItem->item.rotated = qfalse;
00423     }
00424 
00425     /* Actually remove the item from the 'from' container (if it wasn't already removed). */
00426     if (!alreadyRemovedSource)
00427         if (!self->RemoveFromInventory(self, i, from, fItem))
00428             return IA_NONE;
00429 
00430     /* successful */
00431     if (TU)
00432         *TU -= time;
00433 
00434     assert(self->cacheItem.t);
00435     ic = self->AddToInventory(self, i, self->cacheItem, to, tx, ty, 1);
00436 
00437     /* return data */
00438     if (icp) {
00439         assert(ic);
00440         *icp = ic;
00441     }
00442 
00443     if (INV_IsArmourDef(to)) {
00444         assert(INV_IsArmour(self->cacheItem.t));
00445         return IA_ARMOUR;
00446     } else
00447         return IA_MOVE;
00448 }
00449 
00458 static qboolean I_TryAddToInventory (inventoryInterface_t* self, inventory_t* const inv, item_t item, const invDef_t * container)
00459 {
00460     int x, y;
00461 
00462     INVSH_FindSpace(inv, &item, container, &x, &y, NULL);
00463 
00464     if (x == NONE) {
00465         assert(y == NONE);
00466         return qfalse;
00467     } else {
00468         const int checkedTo = INVSH_CheckToInventory(inv, item.t, container, x, y, NULL);
00469         if (!checkedTo)
00470             return qfalse;
00471         else if (checkedTo == INV_FITS_ONLY_ROTATED)
00472             item.rotated = qtrue;
00473         else
00474             item.rotated = qfalse;
00475 
00476         return self->AddToInventory(self, inv, item, container, x, y, 1) != NULL;
00477     }
00478 }
00479 
00489 static void I_EmptyContainer (inventoryInterface_t* self, inventory_t* const i, const invDef_t * container)
00490 {
00491     invList_t *ic;
00492 
00493     ic = i->c[container->id];
00494 
00495     while (ic) {
00496         invList_t *old = ic;
00497         ic = ic->next;
00498         I_RemoveInvList(self, old);
00499     }
00500 
00501     i->c[container->id] = NULL;
00502 }
00503 
00511 static void I_DestroyInventory (inventoryInterface_t* self, inventory_t* const i)
00512 {
00513     containerIndex_t container;
00514 
00515     if (!i)
00516         return;
00517 
00518     for (container = 0; container < self->csi->numIDs; container++) {
00519         const invDef_t *invDef = &self->csi->ids[container];
00520         if (!invDef->temp)
00521             self->EmptyContainer(self, i, invDef);
00522     }
00523 
00524     memset(i, 0, sizeof(*i));
00525 }
00526 
00527 
00528 #define WEAPONLESS_BONUS    0.4     /* if you got neither primary nor secondary weapon, this is the chance to retry to get one (before trying to get grenades or blades) */
00529 
00538 static int I_PackAmmoAndWeapon (inventoryInterface_t *self, inventory_t* const inv, objDef_t* weapon, int missedPrimary, const equipDef_t *ed)
00539 {
00540     objDef_t *ammo = NULL;
00541     item_t item = {NONE_AMMO, NULL, NULL, 0, 0};
00542     qboolean allowLeft;
00543     qboolean packed;
00544     int ammoMult = 1;
00545 
00546     assert(!INV_IsArmour(weapon));
00547     item.t = weapon;
00548 
00549     /* are we going to allow trying the left hand */
00550     allowLeft = !(inv->c[self->csi->idRight] && inv->c[self->csi->idRight]->item.t->fireTwoHanded);
00551 
00552     if (!weapon->reload) {
00553         item.m = item.t; /* no ammo needed, so fire definitions are in t */
00554     } else {
00555         if (weapon->oneshot) {
00556             /* The weapon provides its own ammo (i.e. it is charged or loaded in the base.) */
00557             item.a = weapon->ammo;
00558             item.m = weapon;
00559             Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: oneshot weapon '%s' in equipment '%s' (%s).\n",
00560                     weapon->id, ed->name, self->name);
00561         } else {
00562             /* find some suitable ammo for the weapon (we will have at least one if there are ammos for this
00563              * weapon in equipment definition) */
00564             int totalAvailableAmmo = 0;
00565             int i;
00566             for (i = 0; i < self->csi->numODs; i++) {
00567                 objDef_t *obj = INVSH_GetItemByIDX(i);
00568                 if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) {
00569                     totalAvailableAmmo++;
00570                 }
00571             }
00572             if (totalAvailableAmmo) {
00573                 int randNumber = rand() % totalAvailableAmmo;
00574                 for (i = 0; i < self->csi->numODs; i++) {
00575                     objDef_t *obj = INVSH_GetItemByIDX(i);
00576                     if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) {
00577                         randNumber--;
00578                         if (randNumber < 0) {
00579                             ammo = obj;
00580                             break;
00581                         }
00582                     }
00583                 }
00584             }
00585 
00586             if (!ammo) {
00587                 Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n",
00588                         weapon->id, ed->name, self->name);
00589                 return 0;
00590             }
00591             /* load ammo */
00592             item.a = weapon->ammo;
00593             item.m = ammo;
00594         }
00595     }
00596 
00597     if (!item.m) {
00598         Com_Printf("I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n",
00599                 weapon->id, ed->name, self->name);
00600         return 0;
00601     }
00602 
00603     /* now try to pack the weapon */
00604     packed = self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idRight]);
00605     if (packed)
00606         ammoMult = 3;
00607     if (!packed && allowLeft)
00608         packed = self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idLeft]);
00609     if (!packed)
00610         packed = self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idBelt]);
00611     if (!packed)
00612         packed = self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idHolster]);
00613     if (!packed)
00614         return 0;
00615 
00616 
00617     /* pack some more ammo in the backpack */
00618     if (ammo) {
00619         int num;
00620         int numpacked = 0;
00621 
00622         /* how many clips? */
00623         num = (1 + ed->numItems[ammo->idx])
00624             * (float) (1.0f + missedPrimary / 100.0);
00625 
00626         /* pack some ammo */
00627         while (num--) {
00628             item_t mun = {NONE_AMMO, NULL, NULL, 0, 0};
00629 
00630             mun.t = ammo;
00631             /* ammo to backpack; belt is for knives and grenades */
00632             numpacked += self->TryAddToInventory(self, inv, mun, &self->csi->ids[self->csi->idBackpack]);
00633             /* no problem if no space left; one ammo already loaded */
00634             if (numpacked > ammoMult || numpacked * weapon->ammo > 11)
00635                 break;
00636         }
00637     }
00638 
00639     return qtrue;
00640 }
00641 
00642 
00649 static void I_EquipActorMelee (inventoryInterface_t *self, inventory_t* const inv, const teamDef_t* td)
00650 {
00651     objDef_t *obj;
00652     item_t item;
00653 
00654     assert(td->onlyWeapon);
00655 
00656     /* Get weapon */
00657     obj = td->onlyWeapon;
00658 
00659     /* Prepare item. This kind of item has no ammo, fire definitions are in item.t. */
00660     item.t = obj;
00661     item.m = item.t;
00662     item.a = NONE_AMMO;
00663     /* Every melee actor weapon definition is firetwohanded, add to right hand. */
00664     if (!obj->fireTwoHanded)
00665         Sys_Error("INVSH_EquipActorMelee: melee weapon %s for team %s is not firetwohanded! (%s)\n",
00666                 obj->id, td->id, self->name);
00667     self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idRight]);
00668 }
00669 
00676 static void I_EquipActorRobot (inventoryInterface_t *self, inventory_t* const inv, objDef_t* weapon)
00677 {
00678     item_t item;
00679 
00680     assert(weapon);
00681 
00682     /* Prepare weapon in item. */
00683     item.t = weapon;
00684     item.a = NONE_AMMO;
00685 
00686     /* Get ammo for item/weapon. */
00687     assert(weapon->numAmmos > 0);   /* There _has_ to be at least one ammo-type. */
00688     assert(weapon->ammos[0]);
00689     item.m = weapon->ammos[0];
00690 
00691     self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idRight]);
00692 }
00693 
00697 typedef enum {
00698     WEAPON_PARTICLE_OR_NORMAL = 0,  
00699     WEAPON_OTHER = 1,               
00700     WEAPON_NO_PRIMARY = 2           
00701 } equipPrimaryWeaponType_t;
00702 
00714 static void I_EquipActor (inventoryInterface_t* self, inventory_t* const inv, const equipDef_t *ed, const teamDef_t* td)
00715 {
00716     int i;
00717     const int numEquip = lengthof(ed->numItems);
00718     int repeat = 0;
00719     const float AKIMBO_CHANCE = 0.3;    
00722     if (td->weapons) {
00723         equipPrimaryWeaponType_t primary = WEAPON_NO_PRIMARY;
00724         int sum;
00725         int missedPrimary = 0; 
00727         objDef_t *primaryWeapon = NULL;
00728         int hasWeapon = 0;
00729         /* Primary weapons */
00730         const int maxWeaponIdx = min(self->csi->numODs - 1, numEquip - 1);
00731         int randNumber = rand() % 100;
00732         for (i = 0; i < maxWeaponIdx; i++) {
00733             objDef_t *obj = INVSH_GetItemByIDX(i);
00734             if (ed->numItems[i] && obj->weapon && obj->fireTwoHanded && obj->isPrimary) {
00735                 randNumber -= ed->numItems[i];
00736                 missedPrimary += ed->numItems[i];
00737                 if (!primaryWeapon && randNumber < 0)
00738                     primaryWeapon = obj;
00739             }
00740         }
00741         /* See if a weapon has been selected. */
00742         if (primaryWeapon) {
00743             hasWeapon += I_PackAmmoAndWeapon(self, inv, primaryWeapon, 0, ed);
00744             if (hasWeapon) {
00745                 int ammo;
00746 
00747                 /* Find the first possible ammo to check damage type. */
00748                 for (ammo = 0; ammo < self->csi->numODs; ammo++)
00749                     if (ed->numItems[ammo] && INVSH_LoadableInWeapon(&self->csi->ods[ammo], primaryWeapon))
00750                         break;
00751                 if (ammo < self->csi->numODs) {
00752                     primary =
00753                         /* To avoid two particle weapons. */
00754                         !(self->csi->ods[ammo].dmgtype == self->csi->damParticle)
00755                         /* To avoid SMG + Assault Rifle */
00756                         && !(self->csi->ods[ammo].dmgtype == self->csi->damNormal);
00757                 }
00758                 /* reset missedPrimary: we got a primary weapon */
00759                 missedPrimary = 0;
00760             } else {
00761                 Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: primary weapon '%s' couldn't be equipped in equipment '%s' (%s).\n",
00762                         primaryWeapon->id, ed->name, self->name);
00763                 repeat = WEAPONLESS_BONUS > frand();
00764             }
00765         }
00766 
00767         /* Sidearms (secondary weapons with reload). */
00768         do {
00769             int randNumber = rand() % 100;
00770             objDef_t *secondaryWeapon = NULL;
00771             for (i = 0; i < self->csi->numODs; i++) {
00772                 objDef_t *obj = INVSH_GetItemByIDX(i);
00773                 if (ed->numItems[i] && obj->weapon && obj->reload && !obj->deplete && obj->isSecondary) {
00774                     randNumber -= ed->numItems[i] / (primary == WEAPON_PARTICLE_OR_NORMAL ? 2 : 1);
00775                     if (randNumber < 0) {
00776                         secondaryWeapon = obj;
00777                         break;
00778                     }
00779                 }
00780             }
00781 
00782             if (secondaryWeapon) {
00783                 hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, missedPrimary, ed);
00784                 if (hasWeapon) {
00785                     /* Try to get the second akimbo pistol if no primary weapon. */
00786                     if (primary == WEAPON_NO_PRIMARY && !secondaryWeapon->fireTwoHanded && frand() < AKIMBO_CHANCE) {
00787                         I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed);
00788                     }
00789                 }
00790             }
00791         } while (!hasWeapon && repeat--);
00792 
00793         /* Misc items and secondary weapons without reload. */
00794         if (!hasWeapon)
00795             repeat = WEAPONLESS_BONUS > frand();
00796         else
00797             repeat = 0;
00798         /* Misc object probability can be bigger than 100 -- you're sure to
00799          * have one misc if it fits your backpack */
00800         sum = 0;
00801         for (i = 0; i < self->csi->numODs; i++) {
00802             objDef_t *obj = INVSH_GetItemByIDX(i);
00803             if (ed->numItems[i] && ((obj->weapon && obj->isSecondary
00804              && (!obj->reload || obj->deplete)) || obj->isMisc)) {
00805                 /* if ed->num[i] is greater than 100, the first number is the number of items you'll get:
00806                  * don't take it into account for probability
00807                  * Make sure that the probability is at least one if an item can be selected */
00808                 sum += ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0;
00809             }
00810         }
00811         if (sum) {
00812             do {
00813                 int randNumber = rand() % sum;
00814                 objDef_t *secondaryWeapon = NULL;
00815                 for (i = 0; i < self->csi->numODs; i++) {
00816                     objDef_t *obj = INVSH_GetItemByIDX(i);
00817                     if (ed->numItems[i] && ((obj->weapon && obj->isSecondary
00818                      && (!obj->reload || obj->deplete)) || obj->isMisc)) {
00819                         randNumber -= ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0;
00820                         if (randNumber < 0) {
00821                             secondaryWeapon = obj;
00822                             break;
00823                         }
00824                     }
00825                 }
00826 
00827                 if (secondaryWeapon) {
00828                     int num = ed->numItems[secondaryWeapon->idx] / 100 + (ed->numItems[secondaryWeapon->idx] % 100 >= 100 * frand());
00829                     while (num--) {
00830                         hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed);
00831                     }
00832                 }
00833             } while (repeat--); /* Gives more if no serious weapons. */
00834         }
00835 
00836         /* If no weapon at all, bad guys will always find a blade to wield. */
00837         if (!hasWeapon) {
00838             int maxPrice = 0;
00839             objDef_t *blade = NULL;
00840             Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: no weapon picked in equipment '%s', defaulting to the most expensive secondary weapon without reload. (%s)\n",
00841                     ed->name, self->name);
00842             for (i = 0; i < self->csi->numODs; i++) {
00843                 objDef_t *obj = INVSH_GetItemByIDX(i);
00844                 if (ed->numItems[i] && obj->weapon && obj->isSecondary && !obj->reload) {
00845                     if (obj->price > maxPrice) {
00846                         maxPrice = obj->price;
00847                         blade = obj;
00848                     }
00849                 }
00850             }
00851             if (maxPrice)
00852                 hasWeapon += I_PackAmmoAndWeapon(self, inv, blade, 0, ed);
00853         }
00854         /* If still no weapon, something is broken, or no blades in equipment. */
00855         if (!hasWeapon)
00856             Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: cannot add any weapon; no secondary weapon without reload detected for equipment '%s' (%s).\n",
00857                     ed->name, self->name);
00858 
00859         /* Armour; especially for those without primary weapons. */
00860         repeat = (float) missedPrimary > frand() * 100.0;
00861     } else {
00862         return;
00863     }
00864 
00865     if (td->armour) {
00866         do {
00867             int randNumber = rand() % 100;
00868             for (i = 0; i < self->csi->numODs; i++) {
00869                 objDef_t *armour = INVSH_GetItemByIDX(i);
00870                 if (ed->numItems[i] && INV_IsArmour(armour)) {
00871                     randNumber -= ed->numItems[i];
00872                     if (randNumber < 0) {
00873                         const item_t item = {NONE_AMMO, NULL, armour, 0, 0};
00874                         if (self->TryAddToInventory(self, inv, item, &self->csi->ids[self->csi->idArmour])) {
00875                             repeat = 0;
00876                             break;
00877                         }
00878                     }
00879                 }
00880             }
00881         } while (repeat-- > 0);
00882     } else {
00883         Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: teamdef '%s' may not carry armour (%s)\n",
00884                 td->name, self->name);
00885     }
00886 
00887     {
00888         int randNumber = rand() % 10;
00889         for (i = 0; i < self->csi->numODs; i++) {
00890             if (ed->numItems[i]) {
00891                 objDef_t *miscItem = INVSH_GetItemByIDX(i);
00892                 if (miscItem->isMisc && !miscItem->weapon) {
00893                     randNumber -= ed->numItems[i];
00894                     if (randNumber < 0) {
00895                         const item_t item = {NONE_AMMO, NULL, miscItem, 0, 0};
00896                         containerIndex_t container;
00897                         if (miscItem->headgear)
00898                             container = self->csi->idHeadgear;
00899                         else if (miscItem->extension)
00900                             container = self->csi->idExtension;
00901                         else
00902                             container = self->csi->idBackpack;
00903                         self->TryAddToInventory(self, inv, item, &self->csi->ids[container]);
00904                     }
00905                 }
00906             }
00907         }
00908     }
00909 }
00910 
00915 static int I_GetUsedSlots (inventoryInterface_t* self)
00916 {
00917     int i = 0;
00918     const invList_t* slot = self->invList;
00919     while (slot) {
00920         slot = slot->next;
00921         i++;
00922     }
00923     Com_DPrintf(DEBUG_SHARED, "Used inventory slots %i (%s)\n", i, self->name);
00924     return i;
00925 }
00926 
00937 void INV_InitInventory (const char *name, inventoryInterface_t *interface, csi_t* csi, const inventoryImport_t *import)
00938 {
00939     const item_t item = {NONE_AMMO, NULL, NULL, 0, 0};
00940 
00941     memset(interface, 0, sizeof(*interface));
00942 
00943     interface->import = import;
00944     interface->name = name;
00945     interface->cacheItem = item;
00946     interface->csi = csi;
00947     interface->invList = NULL;
00948 
00949     interface->TryAddToInventory = I_TryAddToInventory;
00950     interface->AddToInventory = I_AddToInventory;
00951     interface->RemoveFromInventory = I_RemoveFromInventory;
00952     interface->MoveInInventory = I_MoveInInventory;
00953     interface->DestroyInventory = I_DestroyInventory;
00954     interface->EmptyContainer = I_EmptyContainer;
00955     interface->EquipActor = I_EquipActor;
00956     interface->EquipActorMelee = I_EquipActorMelee;
00957     interface->EquipActorRobot = I_EquipActorRobot;
00958     interface->GetUsedSlots = I_GetUsedSlots;
00959 }
00960 
00961 void INV_DestroyInventory (inventoryInterface_t *interface)
00962 {
00963     if (interface->import == NULL)
00964         return;
00965     interface->import->FreeAll();
00966     memset(interface, 0, sizeof(*interface));
00967 }

Generated by  doxygen 1.6.2