Is there some way now to preserve inventory items across a death exit?
I found this old thread saying the only way to do that is with (ACS) global variables. Now that we have ZScript, is that still the case, or is there now some better way to deal with it?
Preserving inventory over a death exit
Moderator: GZDoom Developers
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.
Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.
Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
- Graf Zahl
- Lead GZDoom+Raze Developer
- Posts: 49231
- Joined: Sat Jul 19, 2003 10:19 am
- Location: Germany
Re: Preserving inventory over a death exit
You have to store the inventory somewhere safe, because upon death it gets destroyed. With ZScript it should be doable, if you override the player's Die method where you move the inventory to a safe place and PostBeginPlay where you move it back to the new player.
However, this won't work if the game restarts on death. In that case all game related data will be deleted.
However, this won't work if the game restarts on death. In that case all game related data will be deleted.
Re: Preserving inventory over a death exit
Restarts on death, as in, the map changes while the player is dead?
I'm referring to the trick used by some maps where you're teleported into a room full of barrels and a BossBrain. You telefrag a barrel, the barrels explode, you die, the BossBrain dies, and the map ends while you're dead, forcing a pistol start on the next map.
I'm referring to the trick used by some maps where you're teleported into a room full of barrels and a BossBrain. You telefrag a barrel, the barrels explode, you die, the BossBrain dies, and the map ends while you're dead, forcing a pistol start on the next map.
Re: Preserving inventory over a death exit
I found I can transfer a dead player's inventory to a temporary actor using an event handler, but when the map changes, the temporary actor doesn't exist any more!
- Graf Zahl
- Lead GZDoom+Raze Developer
- Posts: 49231
- Joined: Sat Jul 19, 2003 10:19 am
- Location: Germany
Re: Preserving inventory over a death exit
You have to transfer it to some static object that can survive a map change, i.e. a thinker stored in the STAT_STATIC slot.
Re: Preserving inventory over a death exit
Well, that was a rather ugly solution. Here's what I came up with:
It works, mostly, but damn if it isn't verbose. It turns out that I also have to move the inventory items themselves into the STAT_STATIC slot. It also has the rather odd behavior that, if it preserves a weapon across a death exit, the weapon can no longer be selected using the weapon slot buttons or next/previous weapon buttons, only by the “use” command. (I only tested it with a weapon, so I have no idea what other strangeness could result from such a hack.)
I also came up with a much cleaner solution:
This, however, has an even bigger problem: ClearInventory also removes the PlayerPawn's start items, and APlayerPawn::GiveDefaultInventory is not exposed to ZScript! A feature request for that has already been posted.
Code: Select all
class DeathExitInventoryHolder : Actor {
default {
+NOINTERACTION;
}
int PlayerNumber;
override void BeginPlay() {
Super.BeginPlay();
ChangeStatNum(STAT_STATIC);
Console.Printf("DeathExitInventoryHolder for player %d spawned!", PlayerNumber);
}
override void OnDestroy() {
Console.Printf("\cgInventoryHolder for player %d destroyed!", PlayerNumber);
}
static DeathExitInventoryHolder Find(int playerNumber) {
let i = ThinkerIterator.Create("DeathExitInventoryHolder", statnum: STAT_STATIC);
DeathExitInventoryHolder h;
while (h = DeathExitInventoryHolder(i.Next()))
if (h.PlayerNumber == playerNumber)
return h;
return null;
}
static DeathExitInventoryHolder Create(int playerNumber) {
let h = Find(playerNumber);
if (!h) {
Console.Printf("No DeathExitInventoryHolder for player %d found. Creating one.", playerNumber);
h = DeathExitInventoryHolder(Spawn("DeathExitInventoryHolder"));
h.PlayerNumber = playerNumber;
}
else
Console.Printf("\cgInventoryHolder.Create called for player %d, but one already exists!", playerNumber);
return h;
}
}
class DeathExitInterceptor : StaticEventHandler {
override void WorldUnloaded(WorldEvent evt) {
for (let pn = 0; pn < MAXPLAYERS; pn++) {
if (!playeringame[pn])
continue;
let h = DeathExitInventoryHolder.Find(pn);
if (!h) {
Console.Printf("\cgNo DeathExitInventoryHolder for player %d!", pn);
continue;
}
// Fully clear the inventory of the DeathExitInventoryHolder, including UNDROPPABLE items.
h.ClearInventory();
while (h.Inv)
h.RemoveInventory(h.Inv);
if (players[pn].playerstate == PST_DEAD) {
let pc = players[pn].mo;
if (pc.Inv) {
Console.Printf("Player %d is dead at map exit. Saving inventory...", pn, players[pn].mo.Inv.GetClassName());
Inventory inv;
while (inv = pc.Inv) {
Console.Printf("* %s", inv.GetClassName());
pc.RemoveInventory(inv);
if (inv.bUndroppable) {
h.AddInventory(inv);
inv.ChangeStatNum(Thinker.STAT_STATIC);
}
}
}
else
Console.Printf("Player %d is dead, and has no inventory.", pn);
}
}
}
override void PlayerEntered(PlayerEvent evt) {
let pn = evt.PlayerNumber;
let pc = players[pn].mo;
let h = DeathExitInventoryHolder.Find(pn);
if (h) {
if (h.Inv) {
Console.Printf("Player %d entered. Restoring inventory.", pn);
while (h.Inv) {
let i = h.Inv;
h.RemoveInventory(i);
i.ChangeStatNum(Thinker.STAT_INVENTORY);
let handled = false;
for (let pi = pc.Inv; pi; pi = pi.Inv)
if (pi.HandlePickup(i)) {
handled = true;
break;
}
if (!handled)
pc.AddInventory(i);
}
}
else
Console.Printf("Player %d entered, but does not have any inventory waiting.", pn);
}
else {
Console.Printf("Player %d entered, and is new. Allocating inventory holder.", pn);
DeathExitInventoryHolder.Create(pn);
}
}
override void PlayerDisconnected(PlayerEvent evt) {
let pn = evt.PlayerNumber;
Console.Printf("Player %d disconnected. Destroying inventory holder.", pn);
let h = DeathExitInventoryHolder.Find(pn);
if (h)
h.Destroy();
}
}
I also came up with a much cleaner solution:
Code: Select all
class DeathExitInterceptor : StaticEventHandler {
override void WorldUnloaded(WorldEvent evt) {
for (let pn = 0; pn < MAXPLAYERS; pn++)
if (playeringame[pn] && players[pn].playerstate == PST_DEAD) {
Console.Printf("Player %d is dead at level exit. Resurrecting and resetting inventory.", pn);
players[pn].Resurrect();
let pc = players[pn].mo;
pc.ClearInventory();
}
}
}