/** * Remove object 'obj' from pile 'pile'. */ void pile_excise(struct object **pile, struct object *obj) { struct object *prev = obj->prev; struct object *next = obj->next; assert(pile_contains(*pile, obj)); pile_check_integrity("excise [pre]", *pile, obj); /* Special case: unlink top object */ if (*pile == obj) { assert(prev == NULL); /* Invariant - if it's the top of the pile */ *pile = next; } else { assert(obj->prev != NULL); /* Should definitely have a previous one set */ /* Otherwise unlink from the previous */ prev->next = next; obj->prev = NULL; } /* And then unlink from the next */ if (next) { next->prev = prev; obj->next = NULL; } pile_check_integrity("excise [post]", *pile, NULL); }
/** * Check that both the object lists are consistent and relate to locations of * objects correctly */ void object_lists_check_integrity(void) { int i; assert(cave->obj_max == cave_k->obj_max); for (i = 0; i < cave->obj_max; i++) { struct object *obj = cave->objects[i]; struct object *known_obj = cave_k->objects[i]; if (obj) { assert(obj->oidx == i); if (obj->iy && obj->ix) assert(pile_contains(cave->squares[obj->iy][obj->ix].obj, obj)); } if (known_obj) { assert (obj); assert(known_obj == obj->known); if (known_obj->iy && known_obj->ix) assert (pile_contains(cave_k->squares[known_obj->iy][known_obj->ix].obj, known_obj)); assert (known_obj->oidx == i); } } }
/** * Check that a pair of object lists are consistent and relate to locations of * objects correctly */ void object_lists_check_integrity(struct chunk *c, struct chunk *c_k) { int i; assert(c->obj_max == c_k->obj_max); for (i = 0; i < c->obj_max; i++) { struct object *obj = c->objects[i]; struct object *known_obj = c_k->objects[i]; if (obj) { assert(obj->oidx == i); if (obj->iy && obj->ix) assert(pile_contains(c->squares[obj->iy][obj->ix].obj, obj)); } if (known_obj) { assert (obj); if (player->upkeep->playing) { assert(known_obj == obj->known); } if (known_obj->iy && known_obj->ix) assert (pile_contains(c_k->squares[known_obj->iy][known_obj->ix].obj, known_obj)); assert (known_obj->oidx == i); } } }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; char o_name[80]; char label; /* Error check */ if (amt <= 0) return; /* Check it is still held, in case there were two drop commands queued * for this item. This is in theory not ideal, but in practice should * be safe. */ if (!pile_contains(player->gear, obj)) return; /* Get where the object is now */ label = gear_to_label(obj); /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, TRUE); /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", o_name, label); /* Drop it near the player */ drop_near(cave, dropped, 0, py, px, FALSE); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; char o_name[80]; /* Error check */ if (amt <= 0) return; /* This should not happen - ask for report */ if (!pile_contains(player->gear, obj)) { /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); msg("Bug - attempt to drop %s when not held!", o_name); return; } /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, TRUE); /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", o_name, gear_to_label(obj)); /* Drop it near the player */ drop_near(cave, dropped, 0, py, px, FALSE); event_signal(EVENT_INVENTORY); }
/** * Return TRUE if the given object is on the floor at this grid */ bool square_holds_object(struct chunk *c, int y, int x, struct object *obj) { return pile_contains(square_object(c, y, x), obj); }
bool object_is_carried(struct player *p, const struct object *obj) { return pile_contains(p->gear, obj); }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; bool none_left = FALSE; bool quiver = FALSE; char name[80]; char label; /* Error check */ if (amt <= 0) return; /* Check it is still held, in case there were two drop commands queued * for this item. This is in theory not ideal, but in practice should * be safe. */ if (!pile_contains(player->gear, obj)) return; /* Get where the object is now */ label = gear_to_label(obj); /* Is it in the quiver? */ if (object_is_in_quiver(player, obj)) quiver = TRUE; /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment, don't combine */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, FALSE, &none_left); /* Describe the dropped object */ object_desc(name, sizeof(name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", name, label); /* Describe what's left */ if (dropped->artifact) { object_desc(name, sizeof(name), dropped, ODESC_FULL | ODESC_SINGULAR); msg("You no longer have the %s (%c).", name, label); } else if (none_left) { /* Play silly games to get the right description */ int number = dropped->number; dropped->number = 0; object_desc(name, sizeof(name), dropped, ODESC_PREFIX | ODESC_FULL); msg("You have %s (%c).", name, label); dropped->number = number; } else { object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); msg("You have %s (%c).", name, label); } /* Drop it near the player */ drop_near(cave, dropped, 0, py, px, FALSE); /* Sound for quiver objects */ if (quiver) sound(MSG_QUIVER); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/** * Wield or wear a single item from the pack or floor */ void inven_wield(struct object *obj, int slot) { struct object *wielded, *old = player->body.slots[slot].obj; const char *fmt; char o_name[80]; /* Increase equipment counter if empty slot */ if (old == NULL) player->upkeep->equip_cnt++; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Split off a new object if necessary */ if (obj->number > 1) { /* Split off a new single object */ wielded = object_split(obj, 1); /* If it's a gear object, give the split item a list entry */ if (pile_contains(player->gear, obj)) { wielded->next = obj->next; obj->next = wielded; wielded->prev = obj; if (wielded->next) (wielded->next)->prev = wielded; } } else wielded = obj; /* Carry floor items, don't allow combining */ if (square_holds_object(cave, player->py, player->px, wielded)) { square_excise_object(cave, player->py, player->px, wielded); inven_carry(player, wielded, FALSE, FALSE); } /* Wear the new stuff */ player->body.slots[slot].obj = wielded; /* Do any ID-on-wield */ object_notice_on_wield(wielded); /* Where is the item now */ if (tval_is_melee_weapon(wielded)) fmt = "You are wielding %s (%c)."; else if (wielded->tval == TV_BOW) fmt = "You are shooting with %s (%c)."; else if (tval_is_light(wielded)) fmt = "Your light source is %s (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), wielded, ODESC_PREFIX | ODESC_FULL); /* Message */ msgt(MSG_WIELD, fmt, o_name, I2A(slot)); /* Cursed! */ if (cursed_p(wielded->flags)) { /* Warn the player */ msgt(MSG_CURSED, "Oops! It feels deathly cold!"); /* Sense the object */ object_notice_curses(wielded); } /* See if we have to overflow the pack */ combine_pack(); pack_overflow(old); /* Recalculate bonuses, torch, mana, gear */ player->upkeep->notice |= (PN_IGNORE); player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP | PR_ARMOR); player->upkeep->redraw |= (PR_STATS | PR_HP | PR_MANA | PR_SPEED); /* Disable repeats */ cmd_disable_repeat(); }
/** * Return true if the given object is on the floor at this grid */ bool square_holds_object(struct chunk *c, int y, int x, struct object *obj) { assert(square_in_bounds(c, y, x)); return pile_contains(square_object(c, y, x), obj); }
NOTEARDOWN /* Testing the linked list functions in obj-pile.c */ int test_obj_piles(void *state) { struct object *pile = NULL; struct object *o1 = object_new(); struct object *o2 = object_new(); struct object *o3 = object_new(); struct object *o4 = object_new(); pile_insert(&pile, o1); eq(pile_contains(pile, o1), TRUE); eq(pile_contains(pile, o2), FALSE); ptreq(pile, o1); ptreq(pile_last_item(pile), o1); pile_insert_end(&pile, o2); eq(pile_contains(pile, o1), TRUE); eq(pile_contains(pile, o2), TRUE); eq(pile_contains(pile, o3), FALSE); ptreq(pile, o1); ptreq(pile_last_item(pile), o2); pile_insert_end(&pile, o3); eq(pile_contains(pile, o1), TRUE); eq(pile_contains(pile, o2), TRUE); eq(pile_contains(pile, o3), TRUE); ptreq(pile, o1); ptreq(pile_last_item(pile)->prev, o2); ptreq(pile_last_item(pile), o3); /* Now let's try excision */ /* From the top */ pile_excise(&pile, o1); ptreq(pile, o2); eq(pile_contains(pile, o1), FALSE); /* Now put it back */ pile_insert(&pile, o1); /* From the end */ pile_excise(&pile, o3); ptreq(pile, o1); eq(pile_contains(pile, o3), FALSE); ptreq(pile_last_item(pile), o2); ptreq(pile_last_item(pile)->prev, o1); object_delete(o3); /* Now put it back, and add another */ o3 = object_new(); pile_insert_end(&pile, o3); pile_insert_end(&pile, o4); /* Try removing from the middle */ pile_excise(&pile, o3); ptreq(pile, o1); /* Now the list should look like o1 <-> o2 <-> o4, so check that */ ptreq(o1->prev, NULL); ptreq(o1->next, o2); ptreq(o2->prev, o1); ptreq(o2->next, o4); ptreq(o3->prev, NULL); ptreq(o3->next, NULL); ptreq(o4->prev, o2); ptreq(o4->next, NULL); /* Free up */ object_pile_free(pile); ok; }