=== modified file 'lib/script/object.lua'
--- lib/script/object.lua
+++ lib/script/object.lua
@@ -668,7 +668,7 @@
 	end
 
 	-- The wand is already empty!
-	if object.pval <= 0 then
+	if rc_stack_charges(object) == 0 then
 		flush_fail()
 		msg_print("The wand has no charges left.")
 
@@ -832,7 +832,8 @@
 	end
 
 	-- Still charging?
-	if object.timeout > (object.pval - pval) then
+	if rc_stack_charges(object) < object.number then
+
 		flush_fail()
 
 		if object.number == 1 then
@@ -939,8 +940,11 @@
 	end
 
 	-- Drain the charge
+	--
+	-- This is horrible, and not the correct place for such things to take
+	-- place.
 	if used_charge == true then
-		object.timeout = object.timeout + pval
+		rc_stack_use_charge(object)
 	end
 
 	return ident, used_charge

=== modified file 'src/cmd2.c'
--- src/cmd2.c
+++ src/cmd2.c
@@ -216,7 +216,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Small chests often drop gold */
 		if (tiny && (rand_int(100) < 75))
@@ -2687,6 +2687,9 @@
 	int chance, tdam, tdis;
 	int mul, div;
 
+	int stack_idx;
+	bool stack_v;
+
 	object_type *o_ptr;
 
 	object_type *i_ptr;
@@ -2722,6 +2725,16 @@
 		o_ptr = &o_list[0 - item];
 	}
 
+	/* Stacking: find out whether it's an rc_stackable item */
+	stack_v = rc_stackable(o_ptr);
+
+	/* Stacking: get just one of the stack (or abort) */
+	if (stack_v)
+	{
+		stack_idx = rc_stack_get_one(o_ptr);
+		if (stack_idx == -1) return;
+	}
+
 
 	/* Get a direction (or cancel) */
 	if (!get_aim_dir(&dir)) return;
@@ -2733,11 +2746,17 @@
 	/* Obtain a local object */
 	object_copy(i_ptr, o_ptr);
 
-	/* Distribute the charges of rods/wands/staves between the stacks */
-	distribute_charges(o_ptr, i_ptr, 1);
+	/* Stacking: Transfer charges */
+	if (stack_v)
+	{
+		i_ptr->extra = NULL;
+		rc_stack_transfer(o_ptr, i_ptr, stack_idx, RC_TRANSFER_INDIVIDUAL);
+	}
 
 	/* Single object */
 	i_ptr->number = 1;
+
+
 
 	/* Reduce and describe inventory */
 	if (item >= 0)

=== modified file 'src/cmd3.c'
--- src/cmd3.c
+++ src/cmd3.c
@@ -338,6 +338,9 @@
 {
 	int item, amt;
 
+	int stack_idx = -1;
+	bool stack_v;
+
 	object_type *o_ptr;
 
 	cptr q, s;
@@ -360,8 +363,25 @@
 		o_ptr = &o_list[0 - item];
 	}
 
+	/* Stacking: find out whether it's an rc_stackable item */
+	stack_v = rc_stackable(o_ptr);
+
+	/* Stacking: get just one of the stack */
+	if (stack_v)
+	{
+		/* Get one */
+		stack_idx = rc_stack_get_one(o_ptr);
+
+		/* Only one to be dropped (or failure) */
+		if (stack_idx == -1) amt = 0;
+		else amt = 1;
+	}
+
 	/* Get a quantity */
-	amt = get_quantity(NULL, o_ptr->number);
+	else
+	{
+		amt = get_quantity(NULL, o_ptr->number);
+	}
 
 	/* Allow user abort */
 	if (amt <= 0) return;
@@ -380,7 +400,7 @@
 	p_ptr->energy_use = 50;
 
 	/* Drop (some of) the item */
-	inven_drop(item, amt);
+	inven_drop(item, amt, stack_idx);
 }
 
 
@@ -391,6 +411,9 @@
 void do_cmd_destroy(void)
 {
 	int item, amt;
+
+	int stack_idx;
+	bool stack_v;
 
 	object_type *o_ptr;
 
@@ -421,8 +444,25 @@
 		o_ptr = &o_list[0 - item];
 	}
 
+	/* Stacking: find out whether it's an rc_stackable item */
+	stack_v = rc_stackable(o_ptr);
+
+	/* Stacking: get just one of the stack */
+	if (stack_v && (o_ptr->tval != TV_ROD))
+	{
+		/* Get one */
+		stack_idx = rc_stack_get_one(o_ptr);
+
+		/* Only one to be destroyed (or failure) */
+		if (stack_idx == -1) amt = 0;
+		else amt = 1;
+	}
+
 	/* Get a quantity */
-	amt = get_quantity(NULL, o_ptr->number);
+	else
+	{
+		amt = get_quantity(NULL, o_ptr->number);
+	}
 
 	/* Allow user abort */
 	if (amt <= 0) return;
@@ -433,13 +473,12 @@
 	/* Obtain a local object */
 	object_copy(i_ptr, o_ptr);
 
-	if ((o_ptr->tval == TV_WAND) ||
-	    (o_ptr->tval == TV_STAFF) ||
-	    (o_ptr->tval == TV_ROD))
-	{
-		/* Calculate the amount of destroyed charges */
-		i_ptr->pval = o_ptr->pval * amt / o_ptr->number;
-	}
+	/* Stacking: add charges to the item to be destroyed for correct messages */
+    if (stack_v && (o_ptr->tval != TV_ROD))
+    {
+        i_ptr->extra = NULL;
+        rc_stack_add(i_ptr, rc_stack_charges(o_ptr, stack_idx));
+    }
 
 	/* Set quantity */
 	i_ptr->number = amt;
@@ -451,7 +490,11 @@
 	if (verify_destroy)
 	{
 		strnfmt(out_val, sizeof(out_val), "Really destroy %s? ", o_name);
-		if (!get_check(out_val)) return;
+		if (!get_check(out_val))
+		{
+			rc_stack_remove(i_ptr, 0);
+			return;
+		}
 	}
 
 	/* Take a turn */
@@ -496,8 +539,17 @@
 	/* Message */
 	message_format(MSG_DESTROY, 0, "You destroy %s.", o_name);
 
-	/* Reduce the charges of rods/wands/staves */
-	reduce_charges(o_ptr, amt);
+	/* Sort out rechargeables */
+	if (stack_v)
+	{
+		/* Rods; we just transfer them in quanitity */
+		if (o_ptr->tval == TV_ROD)
+			rc_stack_transfer(o_ptr, i_ptr, amt, RC_TRANSFER_HIGHEST);
+
+		/* Non-rods; so just remove the old one */
+		else
+			rc_stack_remove(o_ptr, stack_idx);
+	}
 
 	/* Eliminate the item (from the pack) */
 	if (item >= 0)

=== modified file 'src/cmd5.c'
--- src/cmd5.c
+++ src/cmd5.c
@@ -220,7 +220,7 @@
 	i_ptr = &object_type_body;
 
 	/* Prepare the object */
-	object_wipe(i_ptr);
+	object_wipe_empty(i_ptr);
 
 	/* Prepare the object */
 	object_prep(i_ptr, k_idx);

=== modified file 'src/cmd6.c'
--- src/cmd6.c
+++ src/cmd6.c
@@ -412,7 +412,7 @@
 	}
 
 	/* Notice empty staffs */
-	if (o_ptr->pval <= 0)
+	if (rc_stack_charges(o_ptr, -1) == 0)
 	{
 		if (flush_failure) flush();
 		msg_print("The staff has no charges left.");
@@ -453,7 +453,7 @@
 
 
 	/* Use a single charge */
-	o_ptr->pval--;
+	rc_stack_use_charge(o_ptr);
 
 	/* Describe charges in the pack */
 	if (item >= 0)
@@ -545,7 +545,7 @@
 
 
 	/* Use a single charge */
-	o_ptr->pval--;
+	rc_stack_use_charge(o_ptr);
 
 	/* Describe the charges in the pack */
 	if (item >= 0)

=== modified file 'src/defines.h'
--- src/defines.h
+++ src/defines.h
@@ -56,7 +56,7 @@
 #define VERSION_MAJOR	3
 #define VERSION_MINOR	0
 #define VERSION_PATCH	5
-#define VERSION_EXTRA	0
+#define VERSION_EXTRA	1
 
 
 /*
@@ -2421,7 +2421,7 @@
 #define OPT_disturb_panel			22
 #define OPT_disturb_state			23
 #define OPT_disturb_minor			24
-/* xxx OPT_disturb_other */
+#define OPT_stack_total				25
 /* xxx OPT_alert_hitpoint */
 /* xxx OPT_alert_failure */
 #define OPT_verify_destroy			28
@@ -2541,7 +2541,7 @@
 #define disturb_panel			op_ptr->opt[OPT_disturb_panel]
 #define disturb_state			op_ptr->opt[OPT_disturb_state]
 #define disturb_minor			op_ptr->opt[OPT_disturb_minor]
-/* xxx disturb_other */
+#define stack_total				op_ptr->opt[OPT_stack_total]
 /* xxx */
 /* xxx alert_failure */
 #define verify_destroy			op_ptr->opt[OPT_verify_destroy]

=== modified file 'src/dungeon.c'
--- src/dungeon.c
+++ src/dungeon.c
@@ -392,18 +392,27 @@
 			/* Describe (briefly) */
 			object_desc(o_name, sizeof(o_name), o_ptr, FALSE, 0);
 
-			/* Notify the player */
+			/*** Notify the player ***/
+
+			/* Multiple objects in a stack */
 			if (o_ptr->number > 1)
-				msg_format("Your %s have recharged.", o_name);
+			{
+				/* Check for partial rechargings */
+				if (rc_stackable(o_ptr) && (rc_stack_charges(o_ptr, -1) > 0))
+					msg_format("Some of your %s have recharged.", o_name);
+
+				/* Non-rechargeables */
+				else
+					msg_format("Your %s have recharged.", o_name);
+			}
 
 			/* Artifacts */
 			else if (o_ptr->name1)
-			{
 				msg_format("The %s has recharged.", o_name);
-			}
 
 			/* Single, non-artifact items */
-			else msg_format("Your %s has recharged.", o_name);
+			else
+				msg_format("Your %s has recharged.", o_name);
 
 			/* Done */
 			return;
@@ -423,6 +432,7 @@
 {
 	int i;
 
+	bool charge = FALSE;
 	int charged = 0;
 
 	object_type *o_ptr;
@@ -470,29 +480,18 @@
 		/* Skip non-objects */
 		if (!o_ptr->k_idx) continue;
 
-		/* Examine all charging rods */
-		if ((o_ptr->tval == TV_ROD) && (o_ptr->timeout))
-		{
-			/* Determine how many rods are charging */
-			int temp = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval;
-
-			if (temp > o_ptr->number) temp = o_ptr->number;
-
-			/* Decrease timeout by that number */
-			o_ptr->timeout -= temp;
-
-			/* Boundary control */
-			if (o_ptr->timeout < 0) o_ptr->timeout = 0;
-
-			/* Update if any rods are recharged */
-			if (temp > (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval)
-			{
-				/* Update window */
+		/* Charge all rods */
+		if (o_ptr->tval == TV_ROD)
+		{
+			charge = rc_recharge(o_ptr, -1, 1);
+
+			if (charge)
+			{
+				/* Message when something was recharged */
+				recharged_notice(o_ptr);
+
+				/* Add to counter */
 				charged++;
-
-				/* Message if whole stack is recharged, if inscribed with "!!" */
-				if (!(o_ptr->timeout)) recharged_notice(o_ptr);
-
 			}
 		}
 	}
@@ -519,15 +518,9 @@
 		/* Skip dead objects */
 		if (!o_ptr->k_idx) continue;
 
-		/* Recharge rods on the ground */
-		if ((o_ptr->tval == TV_ROD) && (o_ptr->timeout))
-		{
-			/* Charge it */
-			o_ptr->timeout -= o_ptr->number;
-
-			/* Boundary control */
-			if (o_ptr->timeout < 0) o_ptr->timeout = 0;
-		}
+		/* Charge rods on the ground */
+		if (o_ptr->tval == TV_ROD)
+			(void)rc_recharge(o_ptr, -1, 1);
 	}
 }
 

=== modified file 'src/externs.h'
--- src/externs.h
+++ src/externs.h
@@ -496,12 +496,11 @@
 extern void object_tried(object_type *o_ptr);
 extern bool is_blessed(const object_type *o_ptr);
 extern s32b object_value(const object_type *o_ptr);
-extern void distribute_charges(object_type *o_ptr, object_type *i_ptr, int amt);
-extern void reduce_charges(object_type *o_ptr, int amt);
 extern bool object_similar(const object_type *o_ptr, const object_type *j_ptr);
 extern void object_absorb(object_type *o_ptr, const object_type *j_ptr);
 extern s16b lookup_kind(int tval, int sval);
 extern void object_wipe(object_type *o_ptr);
+extern void object_wipe_empty(object_type *o_ptr);
 extern void object_copy(object_type *o_ptr, const object_type *j_ptr);
 extern void object_prep(object_type *o_ptr, int k_idx);
 extern void apply_magic(object_type *o_ptr, int lev, bool okay, bool good, bool great);
@@ -528,13 +527,34 @@
 extern bool inven_carry_okay(const object_type *o_ptr);
 extern s16b inven_carry(object_type *o_ptr);
 extern s16b inven_takeoff(int item, int amt);
-extern void inven_drop(int item, int amt);
+extern void inven_drop(int item, int amt, int idx);
 extern void combine_pack(void);
 extern void reorder_pack(void);
 extern s16b spell_chance(int spell);
 extern bool spell_okay(int spell, bool known);
 extern void print_spells(const byte *spells, int num, int y, int x);
 extern void display_koff(int k_idx);
+
+
+/*** rc_ module, object2.c ***/
+
+/* Flags for rc_stack_transfer() */
+#define RC_TRANSFER_INDIVIDUAL       1
+#define RC_TRANSFER_LOWEST           2
+#define RC_TRANSFER_HIGHEST          3
+#define RC_TRANSFER_ALL              4
+
+void rc_stack_transfer(object_type *source, object_type *dest, int x, int flag);
+void rc_stack_add(object_type *o_ptr, byte new);
+bool rc_stack_remove(object_type *o_ptr, int index);
+bool rc_stackable(const object_type *o_ptr);
+bool rc_recharge(object_type *o_ptr, int idx, int amt);
+void rc_stack_use_charge(object_type *o_ptr);
+int rc_stack_charges(const object_type *o_ptr, int x);
+int rc_stack_drain(object_type *o_ptr, int amt);
+int rc_stack_get_one(const object_type *o_ptr);
+int rc_stack_get_one_random(const object_type *o_ptr);
+
 
 /* save.c */
 extern bool save_player(void);

=== modified file 'src/l-object.pkg'
--- src/l-object.pkg
+++ src/l-object.pkg
@@ -1205,9 +1205,13 @@
 extern bool inven_carry_okay(const object_type *o_ptr);
 extern s16b inven_carry(object_type *o_ptr);
 extern s16b inven_takeoff(int item, int amt);
-extern void inven_drop(int item, int amt);
+extern void inven_drop(int item, int amt, int idx);
 extern void combine_pack(void);
 extern void reorder_pack(void);
+
+/* XXX */
+extern void rc_stack_use_charge(object_type *o_ptr);
+int rc_stack_charges(const object_type *o_ptr, int x = -1);
 
 extern bool object_aware_p(const object_type *o_ptr);
 extern bool object_tried_p(const object_type *o_ptr);

=== modified file 'src/load.c'
--- src/load.c
+++ src/load.c
@@ -12,7 +12,7 @@
 
 
 /*
- * This file loads savefiles from Angband 2.9.X.
+ * This file loads savefiles from Angband 2.9.X and above.
  *
  * We attempt to prevent corrupt savefiles from inducing memory errors.
  *
@@ -103,6 +103,32 @@
 	return (FALSE);
 }
 
+/*
+ * This function determines if the version of the savefile
+ * currently being read is older than version "x.y.z.a".
+ */
+static bool older_than_extra(int x, int y, int z, int a)
+{
+	/* Much older, or much more recent */
+	if (sf_major < x) return (TRUE);
+	if (sf_major > x) return (FALSE);
+
+	/* Distinctly older, or distinctly more recent */
+	if (sf_minor < y) return (TRUE);
+	if (sf_minor > y) return (FALSE);
+
+	/* Barely older, or barely more recent */
+	if (sf_patch < z) return (TRUE);
+	if (sf_patch > z) return (FALSE);
+
+	/* "Bizarrely" older, or bizarrely more recent */
+	if (sf_extra < a) return (TRUE);
+	if (sf_extra > a) return (FALSE);
+
+	/* Identical versions */
+	return (FALSE);
+}
+
 
 /*
  * Hack -- determine if an item is "wearable" (or a missile)
@@ -309,6 +335,27 @@
 	/* Save the inscription */
 	if (buf[0]) o_ptr->note = quark_add(buf);
 
+	/* Check for an "extra" field */
+	if (!older_than_extra(3, 0, 5, 1))
+	{
+		byte extra, charge;
+	 	int i;
+
+		/* Find the type */
+		rd_byte(&extra);
+
+		/* We have a charge record */
+		if (extra == 1)
+		{
+			/* XXX Do something useful */
+			for (i = 0; i < o_ptr->number; i++)
+			{
+				rd_byte(&charge);
+				rc_stack_add(o_ptr, charge);
+			}
+		}
+	}
+
 	/* Obtain the "kind" template */
 	k_ptr = &k_info[o_ptr->k_idx];
 
@@ -321,30 +368,56 @@
 	if (k_ptr->cost <= 0) o_ptr->ident |= (IDENT_BROKEN);
 
 
-	/*
-	 * Ensure that rods and wands get the appropriate pvals,
-	 * and transfer rod charges to timeout.
-	 * this test should only be passed once, the first
-	 * time the file is open with ROD/WAND stacking code
-	 * It could change the timeout improperly if the PVAL (time a rod
-	 * takes to charge after use) is changed in object.txt.
-	 * But this is nothing a little resting won't solve.
-	 *
-	 * -JG-
-	 */
-	if ((o_ptr->tval == TV_ROD) &&
-	    (o_ptr->pval - (k_ptr->pval * o_ptr->number) != 0))
-	{
-		o_ptr->timeout = o_ptr->pval;
-		o_ptr->pval = k_ptr->pval * o_ptr->number;
-	}
-
-	if (older_than(3, 0, 4))
-	{
-		/* Recalculate charges of stacked wands and staves */
-		if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
-		{
-			o_ptr->pval = o_ptr->pval * o_ptr->number;
+
+	/* Load both previously LM-patched stacking and unstacking versions... */
+	if (older_than_extra(3, 0, 5, 1))
+	{
+		/* Pre-LM stacking */
+		if (older_than(3, 0, 4))
+		{
+			int i;
+
+			/* Recalculate charges of stacked wands and staves */
+			if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+			{
+				for (i = 0; i < o_ptr->number; i++)
+					rc_stack_add(o_ptr, o_ptr->pval);
+			}
+
+			else if (o_ptr->tval == TV_ROD)
+			{
+				/* For rods, add the appropriate number of full charges */
+				for (i = 0; i < o_ptr->number; i++)
+					rc_stack_add(o_ptr, 0);
+			}
+
+		}
+
+		else
+		{
+			int i;
+
+			/*
+			 * We are dealing with an LM-stacking savefile.
+			 *
+			 * This means that the pval for wands and staves contains the sum
+			 * of all the charges contained in the wand/staff stack.
+			 *
+			 * For simplicity's sake, all rods are imported as fully charged.
+			 */
+			if ((o_ptr->tval == TV_STAFF) || (o_ptr->sval == TV_WAND))
+			{
+				/* Add the average amount for each one */
+				for (i = 0; i < o_ptr->number; i++)
+					rc_stack_add(o_ptr, o_ptr->pval / o_ptr->number);
+			}
+
+			else if (o_ptr->tval == TV_ROD)
+			{
+				/* For rods, add the appropriate number of full charges */
+				for (i = 0; i < o_ptr->number; i++)
+					rc_stack_add(o_ptr, 0);
+			}
 		}
 	}
 
@@ -621,7 +694,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Read the item */
 		if (rd_item(i_ptr))
@@ -1311,7 +1384,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Read the item */
 		if (rd_item(i_ptr))
@@ -1570,7 +1643,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Read the item */
 		if (rd_item(i_ptr))

=== modified file 'src/melee1.c'
--- src/melee1.c
+++ src/melee1.c
@@ -510,7 +510,7 @@
 
 					/* Take damage */
 					take_hit(damage, ddesc);
-
+	
 					/* Find an item */
 					for (k = 0; k < 10; k++)
 					{
@@ -522,19 +522,14 @@
 
 						/* Skip non-objects */
 						if (!o_ptr->k_idx) continue;
+
 
 						/* Drain charged wands/staves */
 						if ((o_ptr->tval == TV_STAFF) ||
 						    (o_ptr->tval == TV_WAND))
 						{
-							/* Charged? */
-							if (o_ptr->pval)
-							{
-								drained = o_ptr->pval;
-
-								/* Uncharge */
-								o_ptr->pval = 0;
-							}
+							/* Drain */
+							drained = rc_stack_drain(o_ptr, 15);
 						}
 
 						if (drained)
@@ -683,11 +678,15 @@
 						/* Modify number */
 						i_ptr->number = 1;
 
-						/* Hack -- If a rod, staff, or wand, allocate total
-						 * maximum timeouts or charges between those
-						 * stolen and those missed. -LM-
-						 */
-						distribute_charges(o_ptr, i_ptr, 1);
+						/* XXX Stacking */
+						if (rc_stackable(o_ptr))
+						{
+							byte idx;
+
+							i_ptr->extra = NULL;
+							idx = rc_stack_get_one_random(o_ptr);
+							rc_stack_transfer(o_ptr, i_ptr, idx, RC_TRANSFER_INDIVIDUAL);
+						}
 
 						/* Carry the object */
 						(void)monster_carry(m_idx, i_ptr);

=== modified file 'src/object1.c'
--- src/object1.c
+++ src/object1.c
@@ -1432,42 +1432,89 @@
 	    ((o_ptr->tval == TV_STAFF) ||
 	     (o_ptr->tval == TV_WAND)))
 	{
-		/* Dump " (N charges)" */
-		object_desc_chr_macro(t, ' ');
-		object_desc_chr_macro(t, p1);
-		object_desc_num_macro(t, o_ptr->pval);
-		object_desc_str_macro(t, " charge");
-		if (o_ptr->pval != 1)
-		{
-			object_desc_chr_macro(t, 's');
-		}
-		object_desc_chr_macro(t, p2);
+		int charge, old_charge;
+		int cnt, i;
+		byte *charges = o_ptr->extra;
+
+		/* If the item has no charges, we just don't bother */
+		if (o_ptr->number)
+		{
+			object_desc_chr_macro(t, ' ');
+			object_desc_chr_macro(t, p1);
+
+			/* Display individual charges */
+			if (!stack_total)
+			{
+				/* Is this wasteful, or even neccessary? */
+				byte out_charges[99];
+				byte out_amount[99];
+
+				old_charge = -1;
+
+				/* Loop through all charges */
+				for (i = 0, cnt = -1; i < o_ptr->number; i++)
+				{
+					if (old_charge != charges[i])
+					{
+						cnt++;
+						out_amount[cnt] = 0;
+						out_charges[cnt] = charges[i];
+					}
+
+					/* Increment count */
+					out_amount[cnt]++;
+					old_charge = charges[i];
+				}
+
+				/* Iterate through all charge records */
+				for (i = 0; i <= cnt; i++)
+				{
+					/* Output quantity of charge */
+					if (out_amount[i] > 1)
+					{
+						object_desc_num_macro(t, out_amount[i]);
+						object_desc_chr_macro(t, 'x');
+					}
+
+					/* Output amount of charge */
+					object_desc_num_macro(t, out_charges[i]);
+
+					/* Seperator */
+					if (i != cnt) object_desc_str_macro(t, ", ");
+				}
+
+				object_desc_str_macro(t, " charges");
+			}
+
+			/* Show total charges */
+			else
+			{
+				cnt = rc_stack_charges(o_ptr, -1);
+
+				/* Nice and simple -- just append total charges */
+				object_desc_num_macro(t, cnt);
+				object_desc_str_macro(t, " charges total");
+			}
+
+			object_desc_chr_macro(t, p2);
+		}
 	}
 
 	/* Hack -- Rods have a "charging" indicator */
 	else if (known && (o_ptr->tval == TV_ROD))
 	{
-		/* Hack -- Dump " (# charging)" if relevant */
-		if (o_ptr->timeout > 0)
+		/* Let's have a nice verbose variable name for a change */
+		int number_fully_charged = rc_stack_charges(o_ptr, -1);
+
+		/* Dump (# charging) if relevant */
+		if (number_fully_charged < o_ptr->number)
 		{
 			/* Stacks of rods display an exact count of charging rods. */
 			if (o_ptr->number > 1)
 			{
-				/* Paranoia */
-				if (k_ptr->pval == 0) k_ptr->pval = 1;
-
-				/* Find out how many rods are charging, by dividing
-				 * current timeout by each rod's maximum timeout.
-				 * Ensure that any remainder is rounded up.  Display
-				 * very discharged stacks as merely fully discharged.
-				 */
-				power = (o_ptr->timeout + (k_ptr->pval - 1)) / k_ptr->pval;
-
-				if (power > o_ptr->number) power = o_ptr->number;
-
 				/* Display prettily */
 				object_desc_str_macro(t, " (");
-				object_desc_num_macro(t, power);
+				object_desc_num_macro(t, number_fully_charged);
 				object_desc_str_macro(t, " charging)");
 			}
 			else

=== modified file 'src/object2.c'
--- src/object2.c
+++ src/object2.c
@@ -278,10 +278,10 @@
 
 
 	/* Hack -- move object */
-	COPY(&o_list[i2], &o_list[i1], object_type);
+	object_copy(&o_list[i2], &o_list[i1]);
 
 	/* Hack -- wipe hole */
-	object_wipe(o_ptr);
+	object_wipe_empty(o_ptr);
 }
 
 
@@ -995,8 +995,8 @@
 		case TV_WAND:
 		case TV_STAFF:
 		{
-			/* Pay extra for charges, depending on standard number of charges */
-			value += ((value / 20) * (o_ptr->pval / o_ptr->number));
+			/* Pay extra for charges */
+			value += ((value / 20) * rc_stack_charges(o_ptr, -1));
 
 			/* Done */
 			break;
@@ -1150,31 +1150,18 @@
 
 
 
-
-
-/*
- * Determine if an item can "absorb" a second item
- *
- * See "object_absorb()" for the actual "absorption" code.
- *
- * If permitted, we allow weapons/armor to stack, if "known".
- *
- * Missiles will combine if both stacks have the same "known" status.
- * This is done to make unidentified stacks of missiles useful.
- *
- * Food, potions, scrolls, and "easy know" items always stack.
- *
- * Chests, and activatable items, except rods, never stack (for various
- * reasons).
- */
-bool object_similar(const object_type *o_ptr, const object_type *j_ptr)
+/*
+ * Determine if an item can "absorb" a second item, for the player.
+ * 
+ * This is a clone of object_similar(), except allows stacking of wands
+ * and staves.
+ */
+bool object_similar_player(const object_type *o_ptr, const object_type *j_ptr)
 {
 	int total = o_ptr->number + j_ptr->number;
-
 
 	/* Require identical object types */
 	if (o_ptr->k_idx != j_ptr->k_idx) return (0);
-
 
 	/* Analyze the items */
 	switch (o_ptr->tval)
@@ -1200,12 +1187,10 @@
 		case TV_WAND:
 		{
 			/* Require either knowledge or known empty for both wands/staves */
-			if ((!(o_ptr->ident & (IDENT_EMPTY)) &&
-				!object_known_p(o_ptr)) ||
-				(!(j_ptr->ident & (IDENT_EMPTY)) &&
-				!object_known_p(j_ptr))) return(0);
-
-			/* Assume okay */
+			if ((!(o_ptr->ident & (IDENT_EMPTY)) && !object_known_p(o_ptr)) ||
+				(!(j_ptr->ident & (IDENT_EMPTY)) && !object_known_p(j_ptr))) return (0);
+
+			/* Otherwise fine */
 			break;
 		}
 
@@ -1351,6 +1336,204 @@
 
 	/* They match, so they must be similar */
 	return (TRUE);
+
+}
+
+
+
+/*
+ * Determine if an item can "absorb" a second item
+ *
+ * See "object_absorb()" for the actual "absorption" code.
+ *
+ * If permitted, we allow weapons/armor to stack, if "known".
+ *
+ * Missiles will combine if both stacks have the same "known" status.
+ * This is done to make unidentified stacks of missiles useful.
+ *
+ * Food, potions, scrolls, and "easy know" items always stack.
+ *
+ * Chests, and activatable items, except rods, never stack (for various
+ * reasons).
+ */
+bool object_similar(const object_type *o_ptr, const object_type *j_ptr)
+{
+	int total = o_ptr->number + j_ptr->number;
+
+
+	/* Require identical object types */
+	if (o_ptr->k_idx != j_ptr->k_idx) return (0);
+
+
+	/* Analyze the items */
+	switch (o_ptr->tval)
+	{
+		/* Chests */
+		case TV_CHEST:
+		{
+			/* Never okay */
+			return (0);
+		}
+
+		/* Food and Potions and Scrolls */
+		case TV_FOOD:
+		case TV_POTION:
+		case TV_SCROLL:
+		{
+			/* Assume okay */
+			break;
+		}
+
+		/* Staves and Wands */
+		case TV_STAFF:
+		case TV_WAND:
+		{
+			/* Not okay */
+			return (0);
+		}
+
+		/* Rods */
+		case TV_ROD:
+		{
+			/* Assume okay */
+			break;
+		}
+
+		/* Weapons and Armor */
+		case TV_BOW:
+		case TV_DIGGING:
+		case TV_HAFTED:
+		case TV_POLEARM:
+		case TV_SWORD:
+		case TV_BOOTS:
+		case TV_GLOVES:
+		case TV_HELM:
+		case TV_CROWN:
+		case TV_SHIELD:
+		case TV_CLOAK:
+		case TV_SOFT_ARMOR:
+		case TV_HARD_ARMOR:
+		case TV_DRAG_ARMOR:
+		{
+			/* Fall through */
+		}
+
+		/* Rings, Amulets, Lites */
+		case TV_RING:
+		case TV_AMULET:
+		case TV_LITE:
+		{
+			/* Require both items to be known */
+			if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+			/* Fall through */
+		}
+
+		/* Missiles */
+		case TV_BOLT:
+		case TV_ARROW:
+		case TV_SHOT:
+		{
+			/* Require identical knowledge of both items */
+			if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (0);
+
+			/* Require identical "bonuses" */
+			if (o_ptr->to_h != j_ptr->to_h) return (FALSE);
+			if (o_ptr->to_d != j_ptr->to_d) return (FALSE);
+			if (o_ptr->to_a != j_ptr->to_a) return (FALSE);
+
+			/* Require identical "pval" code */
+			if (o_ptr->pval != j_ptr->pval) return (FALSE);
+
+			/* Require identical "artifact" names */
+			if (o_ptr->name1 != j_ptr->name1) return (FALSE);
+
+			/* Require identical "ego-item" names */
+			if (o_ptr->name2 != j_ptr->name2) return (FALSE);
+
+			/* Hack -- Never stack "powerful" items */
+			if (o_ptr->xtra1 || j_ptr->xtra1) return (FALSE);
+
+			/* Hack -- Never stack recharging items */
+			if (o_ptr->timeout || j_ptr->timeout) return (FALSE);
+
+			/* Require identical "values" */
+			if (o_ptr->ac != j_ptr->ac) return (FALSE);
+			if (o_ptr->dd != j_ptr->dd) return (FALSE);
+			if (o_ptr->ds != j_ptr->ds) return (FALSE);
+
+			/* Probably okay */
+			break;
+		}
+
+		/* Various */
+		default:
+		{
+			/* Require knowledge */
+			if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+			/* Probably okay */
+			break;
+		}
+	}
+
+
+	/* Hack -- Require identical "cursed" and "broken" status */
+	if (((o_ptr->ident & (IDENT_CURSED)) != (j_ptr->ident & (IDENT_CURSED))) ||
+	    ((o_ptr->ident & (IDENT_BROKEN)) != (j_ptr->ident & (IDENT_BROKEN))))
+	{
+		return (0);
+	}
+
+
+	/* Hack -- Require compatible inscriptions */
+	if (o_ptr->note != j_ptr->note)
+	{
+		/* Normally require matching inscriptions */
+		if (!stack_force_notes) return (0);
+
+		/* Never combine different inscriptions */
+		if (o_ptr->note && j_ptr->note) return (0);
+	}
+
+
+	/* Hack -- Require compatible "discount" fields */
+	if (o_ptr->discount != j_ptr->discount)
+	{
+		/* Both are (different) special inscriptions */
+		if ((o_ptr->discount >= INSCRIP_NULL) &&
+		    (j_ptr->discount >= INSCRIP_NULL))
+		{
+			/* Normally require matching inscriptions */
+			return (0);
+		}
+
+		/* One is a special inscription, one is a discount or nothing */
+		else if ((o_ptr->discount >= INSCRIP_NULL) ||
+		         (j_ptr->discount >= INSCRIP_NULL))
+		{
+			/* Normally require matching inscriptions */
+			if (!stack_force_notes) return (0);
+
+			/* Hack -- Never merge a special inscription with a discount */
+			if ((o_ptr->discount > 0) && (j_ptr->discount > 0)) return (0);
+		}
+
+		/* One is a discount, one is a (different) discount or nothing */
+		else
+		{
+			/* Normally require matching discounts */
+			if (!stack_force_costs) return (0);
+		}
+	}
+
+
+	/* Maximal "stacking" limit */
+	if (total >= MAX_STACK_SIZE) return (0);
+
+
+	/* They match, so they must be similar */
+	return (TRUE);
 }
 
 
@@ -1372,9 +1555,17 @@
  */
 void object_absorb(object_type *o_ptr, const object_type *j_ptr)
 {
-	object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
 	int total = o_ptr->number + j_ptr->number;
+
+	/* Sort out rechargeables */
+	if (rc_stackable(o_ptr))
+	{
+		/* Transfer all charges */
+		rc_stack_transfer(j_ptr, o_ptr, 0, RC_TRANSFER_ALL);
+
+		/* Free the memory */
+		FREE(j_ptr->extra);
+	}
 
 	/* Add together the item counts */
 	o_ptr->number = ((total < MAX_STACK_SIZE) ? total : (MAX_STACK_SIZE - 1));
@@ -1393,22 +1584,6 @@
 
 	/* Mega-Hack -- Blend "discounts" */
 	if (o_ptr->discount < j_ptr->discount) o_ptr->discount = j_ptr->discount;
-
-	/*
-	 * Hack -- if rods are stacking, re-calculate the
-	 * pvals (maximum timeouts) and current timeouts together
-	 */
-	if (o_ptr->tval == TV_ROD)
-	{
-		o_ptr->pval = total * k_ptr->pval;
-		o_ptr->timeout += j_ptr->timeout;
-	}
-
-	/* Hack -- if wands or staves are stacking, combine the charges */
-	if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
-	{
-		o_ptr->pval += j_ptr->pval;
-	}
 }
 
 
@@ -1438,9 +1613,11 @@
 
 
 /*
- * Wipe an object clean.
- */
-void object_wipe(object_type *o_ptr)
+ * Wipe an object clean.  This differs from object_wipe(): it should only be
+ * used on a blank object (i.e. a local variable, uninitialised).  Otherwise,
+ * there may be memory leaks.
+ */
+void object_wipe_empty(object_type *o_ptr)
 {
 	/* Wipe the structure */
 	(void)WIPE(o_ptr, object_type);
@@ -1448,7 +1625,27 @@
 
 
 /*
+ * Wipe an object clean.
+ */
+void object_wipe(object_type *o_ptr)
+{
+	/* Free the "extra" field */
+	if (o_ptr->extra)
+	{
+		FREE(o_ptr->extra);
+		o_ptr->extra = NULL;
+	}
+
+	/* Wipe the structure */
+	(void)WIPE(o_ptr, object_type);
+}
+
+
+/*
  * Prepare an object based on an existing object
+ *
+ * Note that when copying a charged item, the original and copy will share the
+ * same charges information.  This is useful behaviour.
  */
 void object_copy(object_type *o_ptr, const object_type *j_ptr)
 {
@@ -1871,35 +2068,35 @@
 {
 	switch (o_ptr->sval)
 	{
-		case SV_WAND_HEAL_MONSTER:		o_ptr->pval = randint(20) + 8; break;
-		case SV_WAND_HASTE_MONSTER:		o_ptr->pval = randint(20) + 8; break;
-		case SV_WAND_CLONE_MONSTER:		o_ptr->pval = randint(5)  + 3; break;
-		case SV_WAND_TELEPORT_AWAY:		o_ptr->pval = randint(5)  + 6; break;
-		case SV_WAND_DISARMING:			o_ptr->pval = randint(5)  + 4; break;
-		case SV_WAND_TRAP_DOOR_DEST:	o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_STONE_TO_MUD:		o_ptr->pval = randint(4)  + 3; break;
-		case SV_WAND_LITE:				o_ptr->pval = randint(10) + 6; break;
-		case SV_WAND_SLEEP_MONSTER:		o_ptr->pval = randint(15) + 8; break;
-		case SV_WAND_SLOW_MONSTER:		o_ptr->pval = randint(10) + 6; break;
-		case SV_WAND_CONFUSE_MONSTER:	o_ptr->pval = randint(12) + 6; break;
-		case SV_WAND_FEAR_MONSTER:		o_ptr->pval = randint(5)  + 3; break;
-		case SV_WAND_DRAIN_LIFE:		o_ptr->pval = randint(3)  + 3; break;
-		case SV_WAND_POLYMORPH:			o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_STINKING_CLOUD:	o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_MAGIC_MISSILE:		o_ptr->pval = randint(10) + 6; break;
-		case SV_WAND_ACID_BOLT:			o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_ELEC_BOLT:			o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_FIRE_BOLT:			o_ptr->pval = randint(8)  + 6; break;
-		case SV_WAND_COLD_BOLT:			o_ptr->pval = randint(5)  + 6; break;
-		case SV_WAND_ACID_BALL:			o_ptr->pval = randint(5)  + 2; break;
-		case SV_WAND_ELEC_BALL:			o_ptr->pval = randint(8)  + 4; break;
-		case SV_WAND_FIRE_BALL:			o_ptr->pval = randint(4)  + 2; break;
-		case SV_WAND_COLD_BALL:			o_ptr->pval = randint(6)  + 2; break;
-		case SV_WAND_WONDER:			o_ptr->pval = randint(15) + 8; break;
-		case SV_WAND_ANNIHILATION:		o_ptr->pval = randint(2)  + 1; break;
-		case SV_WAND_DRAGON_FIRE:		o_ptr->pval = randint(3)  + 1; break;
-		case SV_WAND_DRAGON_COLD:		o_ptr->pval = randint(3)  + 1; break;
-		case SV_WAND_DRAGON_BREATH:		o_ptr->pval = randint(3)  + 1; break;
+		case SV_WAND_HEAL_MONSTER:		rc_stack_add(o_ptr, randint(20) + 8); break;
+		case SV_WAND_HASTE_MONSTER:		rc_stack_add(o_ptr, randint(20) + 8); break;
+		case SV_WAND_CLONE_MONSTER:		rc_stack_add(o_ptr, randint(5)  + 3); break;
+		case SV_WAND_TELEPORT_AWAY:		rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_WAND_DISARMING:			rc_stack_add(o_ptr, randint(5)  + 4); break;
+		case SV_WAND_TRAP_DOOR_DEST:	rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_STONE_TO_MUD:		rc_stack_add(o_ptr, randint(4)  + 3); break;
+		case SV_WAND_LITE:				rc_stack_add(o_ptr, randint(10) + 6); break;
+		case SV_WAND_SLEEP_MONSTER:		rc_stack_add(o_ptr, randint(15) + 8); break;
+		case SV_WAND_SLOW_MONSTER:		rc_stack_add(o_ptr, randint(10) + 6); break;
+		case SV_WAND_CONFUSE_MONSTER:	rc_stack_add(o_ptr, randint(12) + 6); break;
+		case SV_WAND_FEAR_MONSTER:		rc_stack_add(o_ptr, randint(5)  + 3); break;
+		case SV_WAND_DRAIN_LIFE:		rc_stack_add(o_ptr, randint(3)  + 3); break;
+		case SV_WAND_POLYMORPH:			rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_STINKING_CLOUD:	rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_MAGIC_MISSILE:		rc_stack_add(o_ptr, randint(10) + 6); break;
+		case SV_WAND_ACID_BOLT:			rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_ELEC_BOLT:			rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_FIRE_BOLT:			rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_WAND_COLD_BOLT:			rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_WAND_ACID_BALL:			rc_stack_add(o_ptr, randint(5)  + 2); break;
+		case SV_WAND_ELEC_BALL:			rc_stack_add(o_ptr, randint(8)  + 4); break;
+		case SV_WAND_FIRE_BALL:			rc_stack_add(o_ptr, randint(4)  + 2); break;
+		case SV_WAND_COLD_BALL:			rc_stack_add(o_ptr, randint(6)  + 2); break;
+		case SV_WAND_WONDER:			rc_stack_add(o_ptr, randint(15) + 8); break;
+		case SV_WAND_ANNIHILATION:		rc_stack_add(o_ptr, randint(2)  + 1); break;
+		case SV_WAND_DRAGON_FIRE:		rc_stack_add(o_ptr, randint(3)  + 1); break;
+		case SV_WAND_DRAGON_COLD:		rc_stack_add(o_ptr, randint(3)  + 1); break;
+		case SV_WAND_DRAGON_BREATH:		rc_stack_add(o_ptr, randint(3)  + 1); break;
 	}
 }
 
@@ -1912,36 +2109,36 @@
 {
 	switch (o_ptr->sval)
 	{
-		case SV_STAFF_DARKNESS:			o_ptr->pval = randint(8)  + 8; break;
-		case SV_STAFF_SLOWNESS:			o_ptr->pval = randint(8)  + 8; break;
-		case SV_STAFF_HASTE_MONSTERS:	o_ptr->pval = randint(8)  + 8; break;
-		case SV_STAFF_SUMMONING:		o_ptr->pval = randint(3)  + 1; break;
-		case SV_STAFF_TELEPORTATION:	o_ptr->pval = randint(4)  + 5; break;
-		case SV_STAFF_IDENTIFY:			o_ptr->pval = randint(15) + 5; break;
-		case SV_STAFF_REMOVE_CURSE:		o_ptr->pval = randint(3)  + 4; break;
-		case SV_STAFF_STARLITE:			o_ptr->pval = randint(5)  + 6; break;
-		case SV_STAFF_LITE:				o_ptr->pval = randint(20) + 8; break;
-		case SV_STAFF_MAPPING:			o_ptr->pval = randint(5)  + 5; break;
-		case SV_STAFF_DETECT_GOLD:		o_ptr->pval = randint(20) + 8; break;
-		case SV_STAFF_DETECT_ITEM:		o_ptr->pval = randint(15) + 6; break;
-		case SV_STAFF_DETECT_TRAP:		o_ptr->pval = randint(5)  + 6; break;
-		case SV_STAFF_DETECT_DOOR:		o_ptr->pval = randint(8)  + 6; break;
-		case SV_STAFF_DETECT_INVIS:		o_ptr->pval = randint(15) + 8; break;
-		case SV_STAFF_DETECT_EVIL:		o_ptr->pval = randint(15) + 8; break;
-		case SV_STAFF_CURE_LIGHT:		o_ptr->pval = randint(5)  + 6; break;
-		case SV_STAFF_CURING:			o_ptr->pval = randint(3)  + 4; break;
-		case SV_STAFF_HEALING:			o_ptr->pval = randint(2)  + 1; break;
-		case SV_STAFF_THE_MAGI:			o_ptr->pval = randint(2)  + 2; break;
-		case SV_STAFF_SLEEP_MONSTERS:	o_ptr->pval = randint(5)  + 6; break;
-		case SV_STAFF_SLOW_MONSTERS:	o_ptr->pval = randint(5)  + 6; break;
-		case SV_STAFF_SPEED:			o_ptr->pval = randint(3)  + 4; break;
-		case SV_STAFF_PROBING:			o_ptr->pval = randint(6)  + 2; break;
-		case SV_STAFF_DISPEL_EVIL:		o_ptr->pval = randint(3)  + 4; break;
-		case SV_STAFF_POWER:			o_ptr->pval = randint(3)  + 1; break;
-		case SV_STAFF_HOLINESS:			o_ptr->pval = randint(2)  + 2; break;
-		case SV_STAFF_BANISHMENT:		o_ptr->pval = randint(2)  + 1; break;
-		case SV_STAFF_EARTHQUAKES:		o_ptr->pval = randint(5)  + 3; break;
-		case SV_STAFF_DESTRUCTION:		o_ptr->pval = randint(3)  + 1; break;
+		case SV_STAFF_DARKNESS:			rc_stack_add(o_ptr, randint(8)  + 8); break;
+		case SV_STAFF_SLOWNESS:			rc_stack_add(o_ptr, randint(8)  + 8); break;
+		case SV_STAFF_HASTE_MONSTERS:	rc_stack_add(o_ptr, randint(8)  + 8); break;
+		case SV_STAFF_SUMMONING:		rc_stack_add(o_ptr, randint(3)  + 1); break;
+		case SV_STAFF_TELEPORTATION:	rc_stack_add(o_ptr, randint(4)  + 5); break;
+		case SV_STAFF_IDENTIFY:			rc_stack_add(o_ptr, randint(15) + 5); break;
+		case SV_STAFF_REMOVE_CURSE:		rc_stack_add(o_ptr, randint(3)  + 4); break;
+		case SV_STAFF_STARLITE:			rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_STAFF_LITE:				rc_stack_add(o_ptr, randint(20) + 8); break;
+		case SV_STAFF_MAPPING:			rc_stack_add(o_ptr, randint(5)  + 5); break;
+		case SV_STAFF_DETECT_GOLD:		rc_stack_add(o_ptr, randint(20) + 8); break;
+		case SV_STAFF_DETECT_ITEM:		rc_stack_add(o_ptr, randint(15) + 6); break;
+		case SV_STAFF_DETECT_TRAP:		rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_STAFF_DETECT_DOOR:		rc_stack_add(o_ptr, randint(8)  + 6); break;
+		case SV_STAFF_DETECT_INVIS:		rc_stack_add(o_ptr, randint(15) + 8); break;
+		case SV_STAFF_DETECT_EVIL:		rc_stack_add(o_ptr, randint(15) + 8); break;
+		case SV_STAFF_CURE_LIGHT:		rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_STAFF_CURING:			rc_stack_add(o_ptr, randint(3)  + 4); break;
+		case SV_STAFF_HEALING:			rc_stack_add(o_ptr, randint(2)  + 1); break;
+		case SV_STAFF_THE_MAGI:			rc_stack_add(o_ptr, randint(2)  + 2); break;
+		case SV_STAFF_SLEEP_MONSTERS:	rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_STAFF_SLOW_MONSTERS:	rc_stack_add(o_ptr, randint(5)  + 6); break;
+		case SV_STAFF_SPEED:			rc_stack_add(o_ptr, randint(3)  + 4); break;
+		case SV_STAFF_PROBING:			rc_stack_add(o_ptr, randint(6)  + 2); break;
+		case SV_STAFF_DISPEL_EVIL:		rc_stack_add(o_ptr, randint(3)  + 4); break;
+		case SV_STAFF_POWER:			rc_stack_add(o_ptr, randint(3)  + 1); break;
+		case SV_STAFF_HOLINESS:			rc_stack_add(o_ptr, randint(2)  + 2); break;
+		case SV_STAFF_BANISHMENT:		rc_stack_add(o_ptr, randint(2)  + 1); break;
+		case SV_STAFF_EARTHQUAKES:		rc_stack_add(o_ptr, randint(5)  + 3); break;
+		case SV_STAFF_DESTRUCTION:		rc_stack_add(o_ptr, randint(3)  + 1); break;
 	}
 }
 
@@ -2556,10 +2753,9 @@
 
 		case TV_ROD:
 		{
-			object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
-			/* Transfer the pval. */
-			o_ptr->pval = k_ptr->pval;
+			/* Add an initial charge (of 0, i.e. full) */
+			rc_stack_add(o_ptr, 0);
+
 			break;
 		}
 
@@ -3415,7 +3611,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Make a good (or great) object (if possible) */
 		if (!make_object(i_ptr, TRUE, great)) continue;
@@ -3444,7 +3640,7 @@
 	i_ptr = &object_type_body;
 
 	/* Wipe the object */
-	object_wipe(i_ptr);
+	object_wipe_empty(i_ptr);
 
 	/* Make an object (if possible) */
 	if (make_object(i_ptr, good, great))
@@ -3477,7 +3673,7 @@
 	i_ptr = &object_type_body;
 
 	/* Wipe the object */
-	object_wipe(i_ptr);
+	object_wipe_empty(i_ptr);
 
 	/* Make some gold */
 	if (make_gold(i_ptr))
@@ -3639,13 +3835,15 @@
 	object_type *o_ptr = &inventory[item];
 
 	/* Require staff/wand */
-	if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+	if ((o_ptr->tval != TV_STAFF) &&
+	    (o_ptr->tval != TV_WAND) &&
+	    (o_ptr->tval != TV_ROD)) return;
 
 	/* Require known item */
 	if (!object_known_p(o_ptr)) return;
 
 	/* Print a message */
-	msg_format("You have %d charge%s remaining.", o_ptr->pval,
+	msg_format("You have %d charge%s remaining.", rc_stack_charges(o_ptr, -1),
 	           (o_ptr->pval != 1) ? "s" : "");
 }
 
@@ -3740,15 +3938,18 @@
 		/* One less item */
 		p_ptr->inven_cnt--;
 
+		/* Erase the empty slot */
+		object_wipe(&inventory[item]);
+
 		/* Slide everything down */
 		for (i = item; i < INVEN_PACK; i++)
 		{
 			/* Hack -- slide object */
-			COPY(&inventory[i], &inventory[i+1], object_type);
-		}
-
-		/* Hack -- wipe hole */
-		(void)WIPE(&inventory[i], object_type);
+			object_copy(&inventory[i], &inventory[i+1]);
+		}
+
+		/* Wipe the hole */
+		object_wipe_empty(&inventory[i]);
 
 		/* Window stuff */
 		p_ptr->window |= (PW_INVEN);
@@ -3788,13 +3989,15 @@
 	object_type *o_ptr = &o_list[item];
 
 	/* Require staff/wand */
-	if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+	if ((o_ptr->tval != TV_STAFF) &&
+	    (o_ptr->tval != TV_WAND) &&
+	    (o_ptr->tval != TV_ROD)) return;
 
 	/* Require known item */
 	if (!object_known_p(o_ptr)) return;
 
 	/* Print a message */
-	msg_format("There are %d charge%s remaining.", o_ptr->pval,
+	msg_format("There are %d charge%s remaining.", rc_stack_charges(o_ptr, -1),
 	           (o_ptr->pval != 1) ? "s" : "");
 }
 
@@ -3876,7 +4079,7 @@
 		if (!j_ptr->k_idx) continue;
 
 		/* Check if the two items can be combined */
-		if (object_similar(j_ptr, o_ptr)) return (TRUE);
+		if (object_similar_player(j_ptr, o_ptr)) return (TRUE);
 	}
 
 	/* Nope */
@@ -3921,7 +4124,7 @@
 		n = j;
 
 		/* Check if the two items can be combined */
-		if (object_similar(j_ptr, o_ptr))
+		if (object_similar_player(j_ptr, o_ptr))
 		{
 			/* Combine the items */
 			object_absorb(j_ptr, o_ptr);
@@ -4022,12 +4225,15 @@
 		}
 
 		/* Wipe the empty slot */
-		object_wipe(&inventory[i]);
+		object_wipe_empty(&inventory[i]);
 	}
 
 
 	/* Copy the item */
 	object_copy(&inventory[i], o_ptr);
+
+	/* Forget te original item's "extra" field */
+	o_ptr->extra = NULL;
 
 	/* Get the new object */
 	j_ptr = &inventory[i];
@@ -4153,8 +4359,12 @@
  * Drop (some of) a non-cursed inventory/equipment item
  *
  * The object will be dropped "near" the current location
- */
-void inven_drop(int item, int amt)
+ *
+ * Note that "idx" is used as the index into the stack, for when dealing with
+ * a stack of rechargeables.  Also note that you can only drop one rechargeable
+ * at a time.
+ */
+void inven_drop(int item, int amt, int idx)
 {
 	int py = p_ptr->py;
 	int px = p_ptr->px;
@@ -4194,8 +4404,12 @@
 	/* Obtain local object */
 	object_copy(i_ptr, o_ptr);
 
-	/* Distribute charges of wands, staves, or rods */
-	distribute_charges(o_ptr, i_ptr, amt);
+	/* Stacking: Transfer charges */
+	if (rc_stackable(o_ptr))
+	{
+		i_ptr->extra = NULL;
+		rc_stack_transfer(o_ptr, i_ptr, idx, RC_TRANSFER_INDIVIDUAL);
+	}
 
 	/* Modify quantity */
 	i_ptr->number = amt;
@@ -4251,7 +4465,7 @@
 			if (!j_ptr->k_idx) continue;
 
 			/* Can we drop "o_ptr" onto "j_ptr"? */
-			if (object_similar(j_ptr, o_ptr))
+			if (object_similar_player(j_ptr, o_ptr))
 			{
 				/* Take note */
 				flag = TRUE;
@@ -4261,12 +4475,15 @@
 
 				/* One object is gone */
 				p_ptr->inven_cnt--;
+
+				/* Make sure we're freeing memory correctly */
+				object_wipe(&inventory[i]);
 
 				/* Slide everything down */
 				for (k = i; k < INVEN_PACK; k++)
 				{
 					/* Hack -- slide object */
-					COPY(&inventory[k], &inventory[k+1], object_type);
+					object_copy(&inventory[k], &inventory[k+1]);
 				}
 
 				/* Hack -- wipe hole */
@@ -4399,61 +4616,670 @@
 }
 
 
-/*
- * Distribute charges of rods, staves, or wands.
- *
- * o_ptr = source item
- * q_ptr = target item, must be of the same type as o_ptr
- * amt   = number of items that are transfered
- */
-void distribute_charges(object_type *o_ptr, object_type *q_ptr, int amt)
-{
-	/*
-	 * Hack -- If rods, staves, or wands are dropped, the total maximum
-	 * timeout or charges need to be allocated between the two stacks.
-	 * If all the items are being dropped, it makes for a neater message
-	 * to leave the original stack's pval alone. -LM-
-	 */
-	if ((o_ptr->tval == TV_WAND) ||
-	    (o_ptr->tval == TV_STAFF) ||
-	    (o_ptr->tval == TV_ROD))
-	{
-		q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
-
-		if (amt < o_ptr->number) o_ptr->pval -= q_ptr->pval;
-
-		/*
-		 * Hack -- Rods also need to have their timeouts distributed.
-		 *
-		 * The dropped stack will accept all time remaining to charge up to
-		 * its maximum.
-		 */
-		if ((o_ptr->tval == TV_ROD) && (o_ptr->timeout))
-		{
-			if (q_ptr->pval > o_ptr->timeout)
-				q_ptr->timeout = o_ptr->timeout;
-			else
-				q_ptr->timeout = q_ptr->pval;
-
-			if (amt < o_ptr->number)
-				o_ptr->timeout -= q_ptr->timeout;
-		}
-	}
-}
-
-
-void reduce_charges(object_type *o_ptr, int amt)
-{
-	/*
-	 * Hack -- If rods or wand are destroyed, the total maximum timeout or
-	 * charges of the stack needs to be reduced, unless all the items are
-	 * being destroyed. -LM-
-	 */
-	if (((o_ptr->tval == TV_WAND) ||
-	     (o_ptr->tval == TV_STAFF) ||
-	     (o_ptr->tval == TV_ROD)) &&
-	    (amt < o_ptr->number))
-	{
-		o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
-	}
-}
+
+/*** Rechargeables stacking ***/
+
+/*
+ * The "new" rechargeables/stick stacking code makes use of the new field
+ * "extra" introduced in 3.0.6-sp1, which is simply an array of byte values,
+ * allocated in blocks of 10, with one member of the array corresponding to
+ * one item's charge.
+ *
+ * It may also be more elegant to introduce rc_stack_remove_group(), which is
+ * given a range of indexes to remove, and rc_stack_add_group(), which is
+ * passed a byte array.  This would probably be a lot more efficient for
+ * rc_stack_transfer(), for example.  XXX
+ *
+ * Should it be in blocks of 10?  This won't cause excessive allocating or 
+ * deallocating, but there may be a more elegant way.  XXX
+ *
+ * There are various functions here, though perhaps the "rc_stack_" prefix is
+ * a little wrong, considering that many of the funtions here can be used on
+ * any rechargeable, stacked or not.  XXX
+ *
+ * Note that because o_ptr->number starts from 1, but indexes start from 0,
+ * there is lots of index conversion.  Maybe macros should be used to make this
+ * clearer.  XXX
+ */
+
+#define INDEX_VALID(object, x)       ((x >= 0) && (x < object->number))
+
+/*** Basics ***/
+
+
+/* Flags for rc_stack_find() */
+#define RC_FIND_LESS       1
+#define RC_FIND_EXACT      2
+
+
+/*
+ * rc_stack_transfer(): Transfer charges from one (stack of) rechargeable(s)
+ * to another.
+ *
+ * The use of x differs, depending; "lowest" and "highest" flags will use x
+ * as the number of charges to transfer, whereas "individual" will use it as 
+ * the index of the charge which should be transferred.
+ */
+void rc_stack_transfer(object_type *source, object_type *dest, int x, int flag)
+{
+	byte src_num = source->number;
+
+	int i;
+	byte tmp;
+
+	/* Error message, guaranteed to scare/confuse almost everyone except me */
+	if (source->extra == dest->extra)
+	{
+		msg_print("Trying to move charges around with two items with the same charge fields!");
+		msg_print("If you're just a normal player, then this doesn't mean much.");
+		msg_print("As you probably realised, really.");
+
+		return;
+	}
+
+	/* All just means "transfer src_num lowest charges" */
+	if (flag == RC_TRANSFER_ALL) x = src_num;
+
+	/* Transfer multiple charges */
+	if (flag == RC_TRANSFER_LOWEST || flag == RC_TRANSFER_HIGHEST ||
+	    flag == RC_TRANSFER_ALL)
+	{
+		if (x > src_num)
+		{
+			msg_print("rc_stack_transfer(): too many charges specified to transfer.");
+			return;
+		}
+
+		if (flag == RC_TRANSFER_HIGHEST)
+		{
+			for (i = src_num - 1; src_num - i < x; i--)
+			{
+				tmp = rc_stack_charges(source, i);
+
+				rc_stack_remove(source, i);
+				rc_stack_add(dest, tmp);
+			}
+		}
+		else
+		{
+			for (i = 0; i < x; i++)
+			{
+				tmp = rc_stack_charges(source, i);
+
+				rc_stack_remove(source, i);
+				rc_stack_add(dest, tmp);
+			}
+		}
+	}
+
+	else if (flag == RC_TRANSFER_INDIVIDUAL)
+	{
+		if (x > src_num - 1)
+		{
+			msg_print("rc_stack_transfer(): individual transfer invalid.");
+			return;
+		}
+
+		tmp = rc_stack_charges(source, x);
+
+		rc_stack_remove(source, x);
+		rc_stack_add(dest, tmp);
+	}
+
+	return;
+}
+
+
+/*
+ * rc_stack_find(): Find something in the o_ptr->extra array.  "Something" can
+ * be the first record with the charge specified, or the closest record below
+ * the charge specified.
+ *
+ * o_ptr is the object used.  charge is the charge to find.  *index is where
+ * the resulting index will be found.  flag specifies the mode of action
+ * (either RC_FIND_LESS or RC_FIND_EXACT).
+ *
+ * Returns TRUE if something was found, FALSE otherwise.
+ *
+ * This function is a simple linear search, as as such is O(n).  This could
+ * be improved, but it's barely worth it, as n will always be below 100, and
+ * iteration is cheap.
+ */
+static bool rc_stack_find(const object_type *o_ptr, byte charge, byte *index, int flag)
+{
+	byte *charges = o_ptr->extra;
+	byte num = o_ptr->number;
+
+	int i;
+
+	/* Paranoia */
+	if (!o_ptr->extra)
+	{
+		msg_print("rc_stack_find() called with no extra field!");
+		return FALSE;
+	}
+
+	/* Get an exact match (simple) */
+	if (flag == RC_FIND_EXACT)
+	{
+		/* Walk through the charges array */
+		for (i = 0; i < num; i++)
+		{
+			/* Have we found it? */
+			if (charges[i] == charge)
+			{
+				/* Set the index */
+				*index = (byte)i;
+
+				/* Return success */
+				return TRUE;
+			}
+		}
+	}
+
+	/* Get the closest match below (a little less simple) */
+	else if (flag == RC_FIND_LESS)
+	{
+		int i;
+
+		/* Walk through the array */
+		for (i = 0; i < num; i++)
+		{
+			/* Found */
+			if (charges[i] > charge)
+				break;
+		}
+
+		/* Return the closest index */
+		*index = (byte)i;
+
+		/* We always find something */
+		return TRUE;
+	}
+
+	else
+	{
+		/* Error imaginatively */
+		msg_print("rc_stack_find(): unrecognized flag.");
+	}
+
+	/* No luck */
+	return FALSE;
+}
+
+
+/*
+ * Create new stack
+ */
+static void rc_stack_alloc(object_type *o_ptr)
+{
+	byte *holder;
+
+	/* Check */
+	if (o_ptr->extra)
+	{
+		msg_print("rc_stack_new() called when o_ptr->extra is already set.");
+		return;
+	}
+
+	/* Allocate space for this structure */
+	o_ptr->extra = ralloc(10 * sizeof *holder);
+
+	return;
+}
+
+
+/*
+ * rc_stack_add(): Add charge in "new" to stack "o_ptr".
+ *
+ * o_ptr->pval may not be set by the time we get here; in which case, do we
+ * only allocate the record, passing "new" as the first charge, and then
+ * break?  XXX
+ */
+void rc_stack_add(object_type *o_ptr, byte new)
+{
+	byte *charges;
+	byte num_charges = o_ptr->number;
+	byte index;
+
+	/* If there's nothing right now, make it, and do a simple addition */
+	if (!o_ptr->extra)
+	{
+		/* Make new stack */
+		rc_stack_alloc(o_ptr);
+
+		/* Set up the charges */
+		charges = o_ptr->extra;
+
+		/* Set up the initial charge */
+		charges[0] = new;
+
+		return;
+	}
+
+
+	/* Decide if we need to allocate more memory */
+	if ((num_charges / 10) < (num_charges + 1) / 10)
+	{
+		int size = num_charges - (num_charges % 10) + 10;
+
+		/* Allocate new memory */
+		charges = ralloc(size * sizeof *charges);
+
+		/* Copy old memory across */
+		memcpy(charges, o_ptr->extra, num_charges);
+
+		/* Deallocate old memory */
+		rnfree(o_ptr->extra);
+
+		/* Set the object to use this new field */
+		o_ptr->extra = charges;
+	}
+	else
+	{
+		/* Just set up the pointer */
+		charges = (byte *) o_ptr->extra;
+	}
+
+	/* Find the closest record */
+	(void)rc_stack_find(o_ptr, new, &index, RC_FIND_LESS);
+
+	/* Move everything to the right of "index" to the right, by one place */
+	memmove(&charges[index + 1], &charges[index], num_charges - index);
+
+	/* Insert new entry */
+	charges[index] = new;
+
+	/* Done */
+	return;
+}
+
+/*
+ * rc_stack_remove(): Remove one charge record from "o_ptr", with specified
+ * charge index
+ *
+ * If the charge doesn't exist, then we error.
+ *
+ * Return TRUE if the item should then be destroyed.
+ */
+bool rc_stack_remove(object_type *o_ptr, int index)
+{
+	byte *charges = o_ptr->extra;
+	byte num_charges = o_ptr->number;
+
+	/* Paranoia */
+	if (!INDEX_VALID(o_ptr, index))
+	{
+		msg_print("rc_stack_remove(): index passed higher than allowed.");
+		return FALSE;
+	}
+
+
+	/* Just remove one */
+	if (o_ptr->number == 1)
+	{
+		/* Remove the extra field */
+		if (charges)
+		{
+			rnfree(o_ptr->extra);
+			o_ptr->extra = NULL;
+		}
+
+		/* Needs destroying */
+		return TRUE;
+	}
+
+	/* Remove one of many */
+	else
+	{
+		/* Move everything to the right of "index" to the left, by one place */
+		memmove(&charges[index], &charges[index + 1], num_charges - index);
+	}
+
+	/* No need to destroy */
+	return FALSE;
+}
+
+/*
+ * rc_stack_remove_group(): Remove a group of indexes from the stack, from s
+ * to e inclusive.
+ *
+ * If the charge doesn't exist, then we error.
+ *
+ * Return TRUE if the item should then be destroyed.
+ */
+static bool rc_stack_remove_group(object_type *o_ptr, int s, int e)
+{
+	byte *charges = o_ptr->extra;
+	byte num_charges = o_ptr->number;
+
+	/* Just remove one record */
+	if (s == e)
+		return rc_stack_remove(o_ptr, s);
+
+	/* Paranoia */
+	if ((e - s) >= num_charges)
+	{
+		msg_print("rc_stack_remove(): index passed higher than allowed.");
+		return FALSE;
+	}
+
+	/* Remove one of many */
+	else
+	{
+		/* Move everything to the right of "s" to the left, by "e" places */
+		memmove(&charges[s], &charges[s + e], num_charges - (e - s) + 1);
+	}
+
+	/* No need to destroy */
+	return FALSE;
+}
+
+
+
+/*** Utility things ***/
+
+/*
+ * rc_stackable(): check if the item is a rechargeable.
+ */
+bool rc_stackable(const object_type *o_ptr)
+{
+	if (o_ptr->tval == TV_ROD || o_ptr->tval == TV_WAND ||
+	    o_ptr->tval == TV_STAFF)
+	{
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/*
+ * rc_recharge(): Recharge o_ptr:
+ *  - if idx == -1, recharge everything in the stack, by amt
+ *  - if idx >= 0, recharge just that index from the stack, by amt
+ *
+ * Return TRUE if things have been completely recharged (only applies to rods).
+ */
+bool rc_recharge(object_type *o_ptr, int idx, int amt)
+{
+	int i;
+	int count = 0;
+	byte charge;
+
+	byte *charges = o_ptr->extra;
+
+	/* Validation: ensure we have a useable value */
+	if (amt < 1)
+	{
+		msg_print("rc_recharge(): negative charge!  Must be an electron.");
+		return FALSE;
+	}
+
+	/* Recharge all in the stack */
+ 	/* XXX Only called for rods, therefore we assume things */
+	if (idx == -1)
+	{
+		/* Scan the stack */
+		for (i = 0; i < o_ptr->number; i++)
+		{
+			if (charges[i] < 0 + amt - 1) charges[i] -= amt;
+			if (charges[i] == 0) count++;
+		}
+	}
+
+	/* Just one */
+	else
+	{
+		/* Validation: make sure we have a valid index */
+		if ((idx < 0) || (idx > o_ptr->number))
+		{
+			msg_print("rc_recharge(): invalid index passed.");
+			return FALSE;
+		}
+
+		/* Get old charge */
+		charge = charges[idx];
+		rc_stack_remove(o_ptr, idx);
+
+		/* Set new number of charges */
+		rc_stack_add(o_ptr, charge + amt);
+	}
+
+	if (count) return TRUE;
+	else return FALSE;
+}
+
+/*
+ * rc_stack_use_charge(): Use one charge of a rechargeable item.
+ */
+void rc_stack_use_charge(object_type *o_ptr)
+{
+	int i;
+	byte *charges = o_ptr->extra;
+	object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+	if (o_ptr->tval == TV_ROD)
+	{
+		/* Remove first record (always fully charged) */
+		rc_stack_remove(o_ptr, 0);
+
+		/* Increase */
+		rc_stack_add(o_ptr, k_ptr->pval);
+	}
+	else
+	{
+		bool found = FALSE;
+
+		/* Find the first nonzero charge */
+		for (i = 0; (i < o_ptr->number) && (!found); i++)
+			if (charges[i] > 0) found = TRUE;
+
+		/* If we've not found something, there's serious trouble... */
+		if (!found)
+		{
+			msg_print("rc_stack_use_charge(): item passed has no charges!");
+			return;
+		}
+
+		/* Use that charge */
+		charges[i - 1]--;
+	}
+}
+
+/*
+ * rc_stack_charges(): Return number of charges in o_ptr (in total), or if
+ * x >= 0, return merely the charge of index x.
+ *
+ * Rods, at present, are horrid.  XXX
+ */
+int rc_stack_charges(const object_type *o_ptr, int x)
+{
+	byte *charges = o_ptr->extra;
+	int i, cnt;
+
+	/* Easy - return a single charge */
+	if ((x > -1) && (x < o_ptr->number - 1))
+	{
+		return charges[x];
+	}
+
+	/* Rods return number of charging */
+	else if (o_ptr->tval == TV_ROD)
+	{
+		/* Count "0" records */
+		for (i = 0, cnt = 0; i < o_ptr->number; i++)
+		{
+			if (charges[i] == 0) cnt++;
+		}
+	}
+
+	else
+	{
+		/* Add up the charges */
+		for (i = 0, cnt = 0; i < o_ptr->number; i++)
+			cnt += charges[i];
+	}
+
+
+	/* Return the count */
+	return cnt;
+}
+
+/*
+ * rc_stack_get_one_random(): Get one random charge index from o_ptr.
+ */
+int rc_stack_get_one_random(const object_type *o_ptr)
+{
+	/* Choose a random member of the stack */
+	return rand_int(o_ptr->number);
+}
+
+
+/*
+ * rc_stack_drain(): "Drain" a rechargeable.
+ *
+ * We choose a rechargeable at random, and drain amt of charges from it,
+ * but never allow it to drop below 0.  Return number of charges drained.
+ */
+int rc_stack_drain(object_type *o_ptr, int amt)
+{
+	byte *charges = o_ptr->extra;
+	byte num_charges = o_ptr->number;
+
+	int i;
+	byte j;
+
+	/* Choose a random member of the stack */
+	i = randint(num_charges);
+
+	/* Extract the value */
+	j = charges[i];
+	rc_stack_remove(o_ptr, i);
+
+	/* Decrease the amount */
+	if (amt > j) amt = j;
+	j -= amt;
+
+	/* Set new number of charges */
+	rc_stack_add(o_ptr, j);
+
+	/* Done */
+	return amt;
+}
+
+/*
+ * rc_stack_get_one(): Given a rechargeable object, prompt the user for which
+ * one to use if it is a stack, or return 0 if there is no stack (only one level
+ * of charge).  Return -1 on user abort.
+ */
+int rc_stack_get_one(const object_type *o_ptr)
+{
+	int i;
+	bool abort = FALSE;
+	byte *charges = o_ptr->extra;
+	const char *chargetext;
+
+	int col = 79 - 20;
+
+	char ch;
+
+	byte out_charges[99];
+	byte out_amount[99];
+	int cnt = 0, old_charge;
+
+	old_charge = -1;
+
+	/* Loop through all charges */
+	for (i = 0, cnt = -1; i < o_ptr->number; i++)
+	{
+		if (old_charge != charges[i])
+		{
+			cnt++;
+			out_amount[cnt] = 0;
+			out_charges[cnt] = charges[i];
+		}
+
+		/* Increment count */
+		out_amount[cnt]++;
+		old_charge = charges[i];
+	}
+
+	/* If there's only one level of charge, no need to prompt */
+	if (cnt == 0) return 0;
+
+	/* Save screen */
+	screen_save();
+
+	/* Now display */
+	while (!abort)
+	{
+		prt(format("Which charge? (a-%c, ESC)", index_to_label(cnt)), 0, 0);
+
+		for (i = 0; i <= cnt; i++)
+		{
+			/* Clear the line */
+			prt("", i + 1, col);
+
+			if (out_charges[i] == 1) chargetext = "charge";
+			else chargetext = "charges";
+
+			/* Prepare index */
+			put_str(format(" %c) %d x %d %s", index_to_label(i),
+			               out_amount[i], out_charges[i], chargetext), i+1, col);
+		}
+
+	    /* Make a "shadow" below the list (only if needed) */
+	    if (i && (i < 23)) prt("", i + 1, col - 1);
+
+		/* Get some user input */
+		ch = inkey();
+
+		switch (ch)
+		{
+			case ESCAPE:
+			{
+				i = -1;
+				abort = TRUE;
+				break;
+			}
+
+			default:
+			{
+				bool verify;
+
+				/* Note verify */
+				verify = (isupper((unsigned char) ch) ? TRUE : FALSE);
+
+				/* Lowercase */
+				ch = tolower((unsigned char) ch);
+
+				/* Convert into index */
+				i = A2I(ch);
+
+				if (i < 0 || i > cnt)
+				{
+					bell("Illegal object choice (stack)!");
+					break;
+				}
+
+				/* Verify the item */
+				if (verify)
+				{
+					prt(format("Try %c? [y/n]", I2A(i)), 0, 0);
+					ch = inkey();
+					ch = tolower((unsigned char) ch);
+					if (ch == 'n') break;
+				}
+
+				/* Now find the first charge in the stack with that number of charges */
+				(void)rc_stack_find(o_ptr, out_amount[i], &i, RC_FIND_EXACT);
+
+				abort = TRUE;
+			}
+		}
+	}
+
+	screen_load();
+	return i;
+}

=== modified file 'src/save.c'
--- src/save.c
+++ src/save.c
@@ -739,6 +739,23 @@
 	{
 		wr_string("");
 	}
+
+	if (o_ptr->extra)
+	{
+		byte *charges = o_ptr->extra;
+		int i;
+
+		/* 1 indicates "charge record" XXX */
+		wr_byte(1);
+
+		for (i = 0; i < o_ptr->number; i++)
+			wr_byte(charges[i]);
+	}
+	else
+	{
+		/* 0 indicates nothing else to read */
+		wr_byte(0);
+	}
 }
 
 

=== modified file 'src/spells1.c'
--- src/spells1.c
+++ src/spells1.c
@@ -797,16 +797,15 @@
 				           o_name, index_to_label(i),
 				           ((amt > 1) ? "were" : "was"));
 
-				/* Hack -- If rods, wands, or staves are destroyed, the total
-				 * maximum timeout or charges of the stack needs to be reduced,
-				 * unless all the items are being destroyed. -LM-
-				 */
-				if (((o_ptr->tval == TV_WAND) ||
-				     (o_ptr->tval == TV_STAFF) ||
-				     (o_ptr->tval == TV_ROD)) &&
-				    (amt < o_ptr->number))
+				/* Stacking XXX */
+				if (rc_stackable(o_ptr))
 				{
-					o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+					int idx, k;
+					for (k = 0; k < amt; k++)
+					{
+						idx = rc_stack_get_one_random(o_ptr);
+						rc_stack_remove(o_ptr, idx);
+					}
 				}
 
 				/* Destroy "amt" items */

=== modified file 'src/spells2.c'
--- src/spells2.c
+++ src/spells2.c
@@ -1,4 +1,4 @@
-/* File: spells2.c */
+ /* File: spells2.c */
 
 /*
  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
@@ -2105,6 +2105,7 @@
 bool recharge(int num)
 {
 	int i, t, item, lev;
+	int idx;
 
 	object_type *o_ptr;
 
@@ -2131,12 +2132,15 @@
 		o_ptr = &o_list[0 - item];
 	}
 
+	/* Get one of that stack (or abort) */
+	idx = rc_stack_get_one(o_ptr);
+	if (idx == -1) return (FALSE);
 
 	/* Extract the object "level" */
 	lev = k_info[o_ptr->k_idx].level;
 
 	/* Recharge power */
-	i = (num + 100 - lev - (10 * (o_ptr->pval / o_ptr->number))) / 15;
+	i = (num + 100 - lev - (10 * rc_stack_charges(o_ptr, idx))) / 15;
 
 	/* Back-fire */
 	if ((i <= 1) || (rand_int(i) == 0))
@@ -2144,8 +2148,8 @@
 		msg_print("The recharge backfires!");
 		msg_print("There is a bright flash of light.");
 
-		/* Reduce the charges of rods/wands/staves */
-		reduce_charges(o_ptr, 1);
+		/* Nuke the one being recharged */
+		rc_stack_remove(o_ptr, idx);
 
 		/* *Identified* items keep the knowledge about the charges */
 		if (!(o_ptr->ident & IDENT_MENTAL))
@@ -2153,6 +2157,7 @@
 			/* We no longer "know" the item */
 			o_ptr->ident &= ~(IDENT_KNOWN);
 		}
+
 
 		/* Reduce and describe inventory */
 		if (item >= 0)
@@ -2161,6 +2166,7 @@
 			inven_item_describe(item);
 			inven_item_optimize(item);
 		}
+
 		/* Reduce and describe floor item */
 		else
 		{
@@ -2177,7 +2183,8 @@
 		t = (num / (lev + 2)) + 1;
 
 		/* Recharge based on the power */
-		if (t > 0) o_ptr->pval += 2 + randint(t);
+		if (t > 0)
+			rc_recharge(o_ptr, idx, 2 + randint(t));
 
 		/* *Identified* items keep the knowledge about the charges */
 		if (!(o_ptr->ident & IDENT_MENTAL))

=== modified file 'src/store.c'
--- src/store.c
+++ src/store.c
@@ -286,8 +286,10 @@
  * Certain "cheap" objects should be created in "piles".
  *
  * Some objects can be sold at a "discount" (in smaller piles).
- *
  * Standard percentage discounts include 10, 25, 50, 75, and 90.
+ *
+ * Note that if we ever "mass produce" rechargeables, we will have
+ * to remember to add appropriate charges.
  */
 static void mass_produce(object_type *o_ptr)
 {
@@ -323,7 +325,7 @@
 		case TV_PRAYER_BOOK:
 		{
 			if (cost <= 50L) size += mass_roll(2, 3);
-			if (cost <= 500L) size += mass_roll(1, 3);
+				if (cost <= 500L) size += mass_roll(1, 3);
 			break;
 		}
 
@@ -392,12 +394,6 @@
 
 	/* Save the total pile size */
 	o_ptr->number = size - (size * discount / 100);
-
-	/* Hack -- rods need to increase PVAL if stacked */
-	if (o_ptr->tval == TV_ROD)
-	{
-		o_ptr->pval = o_ptr->number * k_info[o_ptr->k_idx].pval;
-	}
 }
 
 
@@ -452,11 +448,11 @@
 	/* Different objects cannot be stacked */
 	if (o_ptr->k_idx != j_ptr->k_idx) return (0);
 
-	/* Different pvals cannot be stacked, except for wands, staves, or rods */
-	if ((o_ptr->pval != j_ptr->pval) &&
-	    (o_ptr->tval != TV_WAND) &&
-	    (o_ptr->tval != TV_STAFF) &&
-	    (o_ptr->tval != TV_ROD)) return (0);
+	/* Different pvals cannot be stacked */
+	if (o_ptr->pval != j_ptr->pval) return (0);
+
+	/* Prevent staves and wands from stacking */
+	if ((o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND)) return (0);
 
 	/* Require many identical values */
 	if (o_ptr->to_h != j_ptr->to_h) return (0);
@@ -497,25 +493,14 @@
 static void store_object_absorb(object_type *o_ptr, object_type *j_ptr)
 {
 	int total = o_ptr->number + j_ptr->number;
+	int old_num = o_ptr->number;
+
+	/* Transfer charges */
+	if (o_ptr->tval == TV_ROD)
+		rc_stack_transfer(j_ptr, o_ptr, 0, RC_TRANSFER_ALL);
 
 	/* Combine quantity, lose excess items */
 	o_ptr->number = (total > 99) ? 99 : total;
-
-	/*
-	 * Hack -- if rods are stacking, add the pvals (maximum timeouts)
-	 * and any charging timeouts together
-	 */
-	if (o_ptr->tval == TV_ROD)
-	{
-		o_ptr->pval += j_ptr->pval;
-		o_ptr->timeout += j_ptr->timeout;
-	}
-
-	/* Hack -- if wands/staves are stacking, combine the charges */
-	if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
-	{
-		o_ptr->pval += j_ptr->pval;
-	}
 }
 
 
@@ -534,7 +519,7 @@
 	/* Free space is always usable */
 	if (st_ptr->stock_num < st_ptr->stock_size) return TRUE;
 
-	/* The "home" acts like the player */
+	/* The "home" acts like the player (almost) */
 	if (store_num == STORE_HOME)
 	{
 		/* Check all the objects */
@@ -774,14 +759,17 @@
 	/* Get the object */
 	o_ptr = &st_ptr->stock[item];
 
+	/* Work out the new number */
+	cnt = o_ptr->number + num;
+
 	/* Verify the number */
-	cnt = o_ptr->number + num;
-	if (cnt > 255) cnt = 255;
+	if (cnt > 255)    cnt = 255;
 	else if (cnt < 0) cnt = 0;
-	num = cnt - o_ptr->number;
-
-	/* Save the new number */
-	o_ptr->number += num;
+
+	/* Set it */
+	o_ptr->number = cnt;
+
+	return;
 }
 
 
@@ -812,7 +800,7 @@
 	}
 
 	/* Nuke the final slot */
-	object_wipe(&st_ptr->stock[j]);
+	object_wipe_empty(&st_ptr->stock[j]);
 }
 
 
@@ -882,12 +870,17 @@
 	/* Hack -- sometimes, only destroy a single object */
 	if (rand_int(100) < 50) num = 1;
 
-	/* Hack -- decrement the maximum timeouts and total charges of rods and wands. */
-	if ((o_ptr->tval == TV_ROD) ||
-	    (o_ptr->tval == TV_STAFF) ||
-	    (o_ptr->tval == TV_WAND))
-	{
-		o_ptr->pval -= num * o_ptr->pval / o_ptr->number;
+
+	/* Nuke charges from a stack if it's a rod */
+	if (o_ptr->tval == TV_ROD)
+	{
+		int idx, i;
+
+		for (i = 0; i < num; i++)
+		{
+			idx = rc_stack_get_one_random(o_ptr);
+			rc_stack_remove(o_ptr, idx);
+		}
 	}
 
 	/* Actually destroy (part of) the object */
@@ -1093,11 +1086,7 @@
 		x = price_item(o_ptr, ot_ptr->inflate, FALSE);
 
 		/* Actually draw the price (with tax) */
-		if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) &&
-		    ((o_ptr->pval % o_ptr->number) > 0))
-			sprintf(out_val, "%9ld avg", (long)x);
-		else
-			sprintf(out_val, "%9ld  ", (long)x);
+		sprintf(out_val, "%9ld  ", (long)x);
 
 		put_str(out_val, y, 68);
 	}
@@ -1608,8 +1597,6 @@
 
 
 
-
-
 /*
  * Buy an object from a store
  */
@@ -1674,12 +1661,6 @@
 	/* Get desired object */
 	object_copy(i_ptr, o_ptr);
 
-	/*
-	 * Hack -- If a rod or wand, allocate total maximum timeouts or charges
-	 * between those purchased and left on the shelf.
-	 */
-	reduce_charges(i_ptr, i_ptr->number - amt);
-
 	/* Modify quantity */
 	i_ptr->number = amt;
 
@@ -1697,8 +1678,7 @@
 		object_desc(o_name, sizeof(o_name), i_ptr, TRUE, 3);
 
 		/* Message */
-		msg_format("Buying %s (%c).",
-		           o_name, store_to_label(item));
+		msg_format("Buying %s (%c).", o_name, store_to_label(item));
 		message_flush();
 
 		/* Haggle for a final price */
@@ -1748,14 +1728,6 @@
 				/* Message */
 				msg_format("You have %s (%c).",
 				           o_name, index_to_label(item_new));
-
-				/* Now, reduce the original stack's pval */
-				if ((o_ptr->tval == TV_ROD) ||
-				    (o_ptr->tval == TV_WAND) ||
-				    (o_ptr->tval == TV_STAFF))
-				{
-					o_ptr->pval -= i_ptr->pval;
-				}
 
 				/* Handle stuff */
 				handle_stuff();
@@ -1836,9 +1808,6 @@
 	/* Home is much easier */
 	else
 	{
-		/* Distribute charges of wands, staves, or rods */
-		distribute_charges(o_ptr, i_ptr, amt);
-
 		/* Give it to the player */
 		item_new = inven_carry(i_ptr);
 
@@ -1884,6 +1853,7 @@
 }
 
 
+
 /*
  * Sell an object to the store (or home)
  */
@@ -1891,6 +1861,9 @@
 {
 	int item, item_pos;
 	int amt;
+
+	int idx;
+	bool stack_v;
 
 	s32b price, value, dummy;
 
@@ -1933,7 +1906,6 @@
 		o_ptr = &o_list[0 - item];
 	}
 
-
 	/* Hack -- Cannot remove cursed objects */
 	if ((item >= INVEN_WIELD) && cursed_p(o_ptr))
 	{
@@ -1944,31 +1916,45 @@
 		return;
 	}
 
-	/* Get a quantity */
-	amt = get_quantity(NULL, o_ptr->number);
+	/* Stacking: find out whether it's an rc_stackable item */
+	stack_v = rc_stackable(o_ptr);
+
+	/* Get local object */
+	i_ptr = &object_type_body;
+
+	/* Get desired object */
+	object_copy(i_ptr, o_ptr);
+
+	/* Prompt */
+	if (stack_v && (o_ptr->tval != TV_ROD))
+	{
+		/* Get which item should be sold (or abort) */
+		idx = rc_stack_get_one(o_ptr);
+
+		/* Set number */
+		if (idx == -1) amt = 0;
+		else amt = 1;
+	}
+
+	else
+	{
+		/* Get a quantity */
+		amt = get_quantity(NULL, o_ptr->number);
+	}
 
 	/* Allow user abort */
 	if (amt <= 0) return;
 
-	/* Get local object */
-	i_ptr = &object_type_body;
-
-	/* Get a copy of the object */
-	object_copy(i_ptr, o_ptr);
+	/* Transfer charges */
+	if (stack_v && (o_ptr->tval != TV_ROD))
+	{
+		i_ptr->extra = NULL;
+		rc_stack_add(i_ptr, rc_stack_charges(o_ptr, idx));
+	}
 
 	/* Modify quantity */
 	i_ptr->number = amt;
 
-	/* Hack -- If a rod, wand, or staff, allocate total maximum
-	 * timeouts or charges to those being sold.
-	 */
-	if ((o_ptr->tval == TV_ROD) ||
-	    (o_ptr->tval == TV_WAND) ||
-	    (o_ptr->tval == TV_STAFF))
-	{
-		i_ptr->pval = o_ptr->pval * amt / o_ptr->number;
-	}
-
 	/* Get a full description */
 	object_desc(o_name, sizeof(o_name), i_ptr, TRUE, 3);
 
@@ -1984,6 +1970,9 @@
 		{
 			msg_print("I have not the room in my store to keep it.");
 		}
+
+		rc_stack_remove(i_ptr, idx);
+
 		return;
 	}
 
@@ -2028,23 +2017,21 @@
 
 			p_ptr->redraw |= (PR_EQUIPPY);
 
-			/* Get local object */
-			i_ptr = &object_type_body;
-
-			/* Get a copy of the object */
-			object_copy(i_ptr, o_ptr);
-
-			/* Modify quantity */
-			i_ptr->number = amt;
+			/* Sort out rechargeables */
+			if (stack_v)
+			{
+				/* Rods; we just transfer them in quanitity */
+				if (o_ptr->tval == TV_ROD)
+					rc_stack_transfer(o_ptr, i_ptr, amt, RC_TRANSFER_HIGHEST);
+
+				/* Non-rods; so just remove the old one */
+				else
+					rc_stack_remove(o_ptr, idx);
+			}
 
 			/* The object belongs to the store now */
 			i_ptr->ident |= IDENT_STORE;
 
-			/*
-			 * Hack -- Allocate charges between those wands, staves, or rods
-			 * sold and retained, unless all are being sold.
-			 */
-			distribute_charges(o_ptr, i_ptr, amt);
 
 			/* Get the "actual" value */
 			value = object_value(i_ptr) * i_ptr->number;
@@ -2082,8 +2069,17 @@
 	/* Player is at home */
 	else
 	{
-		/* Distribute charges of wands/staves/rods */
-		distribute_charges(o_ptr, i_ptr, amt);
+		/* Sort out rechargeables */
+		if (stack_v)
+		{
+			/* Rods; we just transfer them in quanitity */
+			if (o_ptr->tval == TV_ROD)
+				rc_stack_transfer(o_ptr, i_ptr, amt, RC_TRANSFER_HIGHEST);
+
+			/* Non-rods; so just remove the old one */
+			else
+				rc_stack_remove(o_ptr, idx);
+		}
 
 		/* Describe */
 		msg_format("You drop %s (%c).", o_name, index_to_label(item));
@@ -2114,9 +2110,9 @@
  */
 static void store_examine(void)
 {
-	int         item;
+	int item;
 	object_type *o_ptr;
-	char        out_val[160];
+	char out_val[160];
 
 
 	/* Empty? */

=== modified file 'src/tables.c'
--- src/tables.c
+++ src/tables.c
@@ -1421,7 +1421,7 @@
 	"disturb_panel",			/* OPT_disturb_panel */
 	"disturb_state",			/* OPT_disturb_state */
 	"disturb_minor",			/* OPT_disturb_minor */
-	NULL,						/* xxx disturb_other */
+	"stack_total",				/* OPT_stack_total */
 	NULL,						/* xxx alert_hitpoint */
 	NULL,						/* xxx alert_failure */
 	"verify_destroy",			/* OPT_verify_destroy */
@@ -1685,7 +1685,7 @@
 	"Disturb whenever map panel changes",		/* OPT_disturb_panel */
 	"Disturb whenever player state changes",	/* OPT_disturb_state */
 	"Disturb whenever boring things happen",	/* OPT_disturb_minor */
-	NULL,										/* xxx disturb_other */
+	"Show sum of charges in a stack of charged items",  /* OPT_stack_total */
 	NULL,										/* xxx alert_hitpoint */
 	NULL,										/* xxx alert_failure */
 	"Verify destruction of objects",			/* OPT_verify_destroy */
@@ -1949,7 +1949,7 @@
 	TRUE,		/* OPT_disturb_panel */
 	TRUE,		/* OPT_disturb_state */
 	TRUE,		/* OPT_disturb_minor */
-	FALSE,		/* xxx disturb_other */
+	TRUE,		/* OPT_stack_total */
 	FALSE,		/* xxx alert_hitpoint */
 	FALSE,		/* xxx alert_failure */
 	TRUE,		/* OPT_verify_destroy */
@@ -2204,7 +2204,7 @@
 		OPT_easy_open,
 		OPT_easy_alter,
 		OPT_easy_floor,
-		OPT_NONE,
+		OPT_stack_total,
 		OPT_NONE,
 		OPT_NONE,
 		OPT_NONE,

=== modified file 'src/types.h'
--- src/types.h
+++ src/types.h
@@ -480,6 +480,8 @@
 
 	u16b note;			/* Inscription index */
 
+	void *extra;		/* Extra field, for various uses */
+
 	s16b next_o_idx;	/* Next object in stack (if any) */
 
 	s16b held_m_idx;	/* Monster holding us (if any) */

=== modified file 'src/use-obj.c'
--- src/use-obj.c
+++ src/use-obj.c
@@ -1839,7 +1839,7 @@
 	}
 
 	/* Drain the charge */
-	if (used_charge) o_ptr->timeout += k_ptr->pval;
+	if (used_charge) rc_stack_use_charge(o_ptr);
 
 	return TRUE;
 }

=== modified file 'src/wizard1.c'
--- src/wizard1.c
+++ src/wizard1.c
@@ -490,7 +490,7 @@
 			i_ptr = &object_type_body;
 
 			/* Wipe the object */
-			object_wipe(i_ptr);
+			object_wipe_empty(i_ptr);
 
 			/* Attempt to "forge" the artifact */
 			if (!make_fake_artifact(i_ptr, (byte)j)) continue;

=== modified file 'src/wizard2.c'
--- src/wizard2.c
+++ src/wizard2.c
@@ -779,7 +779,7 @@
 			i_ptr = &object_type_body;
 
 			/* Wipe the object */
-			object_wipe(i_ptr);
+			object_wipe_empty(i_ptr);
 
 			/* Create an object */
 			make_object(i_ptr, good, great);
@@ -1042,8 +1042,9 @@
 	/* Return if failed */
 	if (!k_idx) return;
 
-	/* Get local object */
+	/* Get local object (and wipe it) */
 	i_ptr = &object_type_body;
+	object_wipe_empty(i_ptr);
 
 	/* Create the item */
 	object_prep(i_ptr, k_idx);
@@ -1077,7 +1078,7 @@
 	i_ptr = &object_type_body;
 
 	/* Wipe the object */
-	object_wipe(i_ptr);
+	object_wipe_empty(i_ptr);
 
 	/* Acquire the "kind" index */
 	k_idx = lookup_kind(a_ptr->tval, a_ptr->sval);

=== modified file 'src/xtra2.c'
--- src/xtra2.c
+++ src/xtra2.c
@@ -2011,7 +2011,7 @@
 		i_ptr = &object_type_body;
 
 		/* Wipe the object */
-		object_wipe(i_ptr);
+		object_wipe_empty(i_ptr);
 
 		/* Make Gold */
 		if (do_gold && (!do_item || (rand_int(100) < 50)))


