Improvement to standard backpack touch

Discuss programming in the QuakeC language.
Post Reply
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Improvement to standard backpack touch

Post by Cobalt »

I have been wanting to do this one for a long time, but the proper plan sort of eludes me. Lets say theres a backpack from a fragged player that has (40) shells , (64) nails and (100) cells. The player touches it, and he already has (90) shells (40) nails and (0) Cells.
The ID code merely gives the player (100) shells, (100) nails and (100) cells, and the backpack disappears. I want the player to get the same ammo, but leave the backpack there containing what he cannot carry: (30) shells, (4) nails. Heres the touch function Im using, which I think is pretty much standard. Any help with some good code, or if anyone knows existing code to do this, thanks.


void () BackpackTouch =
{
local string s;
local float best;
local float old;
local float new;
local float bbest;
local entity stemp;
local float acount;

if (((other.classname != "player") && (other.classname != "bot")))
{
return;
}

if ((other.health <= 0))
{
return;
}
if (((other == self.owner) && ((self.nextthink - time) > 118)))
{
return;
}
acount = 0;
if ((cvar ("temp1") < 65535))
{
if ((other.classname == "player"))
{
sprint (other, "You get ");
}
}
stemp = self;
self = other;
best = W_BestWeapon ();
if (self.classname == "bot")
{
if (stemp.altname == "tossedpack")
fBotSayTeam (": Thanks.\n");
else if ((stemp.ammo_shells + stemp.ammo_nails) > 175 || (stemp.ammo_rockets + stemp.ammo_cells) > 100)
{
bprint (self.talkname);
bprint ("º ");
BotSayMiddle ("Nice Pack ");
if (stemp.owner.team != self.team)
BotSayMiddle (stemp.owner.netname);
BotSayEnd ();

}
}

self = stemp;
other.ammo_shells = (other.ammo_shells + self.ammo_shells);
other.ammo_nails = (other.ammo_nails + self.ammo_nails);
other.ammo_rockets = (other.ammo_rockets + self.ammo_rockets);
other.ammo_cells = (other.ammo_cells + self.ammo_cells);
new = self.items;
if (!new)
new = other.weapon;

old = other.items;
other.items = (other.items | new);
bound_other_ammo ();
if ((other.classname == "player"))
{
if ((cvar ("temp1") < 65535))
{
if (self.ammo_shells)
{
if (acount)
{
sprint (other, ", ");
}
acount = WEAPON_SHOTGUN;
s = ftos (self.ammo_shells);
sprint (other, s);
sprint (other, " shells");
}
if (self.ammo_nails)
{
if (acount)
{
sprint (other, ", ");
}
acount = WEAPON_SHOTGUN;
s = ftos (self.ammo_nails);
sprint (other, s);
sprint (other, " nails");
}
if (self.ammo_rockets)
{
if (acount)
{
sprint (other, ", ");
}
acount = WEAPON_SHOTGUN;
s = ftos (self.ammo_rockets);
sprint (other, s);
sprint (other, " rockets");
}
if (self.ammo_cells)
{
if (acount)
{
sprint (other, ", ");
}
acount = WEAPON_SHOTGUN;
s = ftos (self.ammo_cells);
sprint (other, s);
sprint (other, " cells");
}
sprint (other, "\n");
stuffcmd (other, "bf\n");
}
}
if (((deathmatch & DM_START_SMALL) || (deathmatch & DM_START_BIG)))
{
other.health = (other.health + 5);
}
sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, SUB_Db());
remove (self);
self = other;
if ((other.classname == "player"))
{
if ((WeaponCode (new) <= other.best_wep))
{
if ((self.flags & FL_INWATER))
{
if ((new != IT_LIGHTNING))
{
Deathmatch_Weapon (old, new);
}
}
else
{
Deathmatch_Weapon (old, new);
}
}
}
else
{
Deathmatch_Weapon (old, new);
}
W_SetCurrentAmmo ();
};



Electro
Posts: 312
Joined: Wed Dec 29, 2004 11:25 pm
Location: Brisbane, Australia
Contact:

Post by Electro »

Sorry, the lack of formatting is making it impossible for me to even read. :(
Benjamin Darling
http://www.bendarling.net/

Reflex - In development competitive arena fps combining modern tech with the speed, precision and freedom of 90's shooters.
http://www.reflexfps.net/
Error
InsideQC Staff
Posts: 865
Joined: Fri Nov 05, 2004 5:15 am
Location: VA, USA
Contact:

Post by Error »

doesn't using the

Code: Select all

 bbcode fix this?
lurker
Posts: 2
Joined: Sat Jul 29, 2006 8:22 pm

Post by lurker »

You will want to modify two different parts of the touch function to make the intended change possible.
Part I:

Code: Select all

other.ammo_shells = (other.ammo_shells + self.ammo_shells);
other.ammo_nails = (other.ammo_nails + self.ammo_nails);
other.ammo_rockets = (other.ammo_rockets + self.ammo_rockets);
other.ammo_cells = (other.ammo_cells + self.ammo_cells);

new = self.items;
if (!new)
	new = other.weapon;
old = other.items;
other.items = (other.items | new); 
bound_other_ammo ();
Part II:

Code: Select all

remove(self);
self = other;

Part I notes:
Imagine that the player had 90 shotgun shells and grabbed a large box (40 shells). The player's .ammo_shells would be 130. Id's BackpackTouch() already does this to the player and then uses bound_other_ammo() to cap it. Let's set the player's ammo the same way, but afterward shave the top off and assign it to the backpack. In this case, the backpack should be left with 30 shells (130 on player - 100 limit = 30).

Code: Select all

other.ammo_shells = (other.ammo_shells + self.ammo_shells);
if (other.ammo_shells > 100)
	self.ammo_shells = other.ammo_shells - 100;
As the player may have needed all ammo of a given type, remember to check for that and set the relevant ammo count on the backpack to 0. Otherwise the backpack will become an endless supply of ammo.

Code: Select all

else
	self.ammo_shells = 0;
You will also need to make some changes to the way the "You got ..." messages are generated, since the player can now trigger the touch function without it actually giving the player anything. This is a bit more complicated, so I have provided a full solution below.



Part II notes:
Backpacks are removed when they are touched by players. You will want to remove it only if it is empty. I suggest borrowing the line in DropBackPack() that checks this.

Code: Select all

if (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells))
	remove(self);

Example / Spoilers
To fix the display, I defined 4 local variables to hold the precise ammo count given to the player. I added a check to see if the backpack would need to generate a message and to exit the touch if not. I moved the weapon check after the ammo stuff to make that check possible. Though I didn't add your new functions to it, feel free to do so!


Edit: the same logic now applies to weapons in backpacks (remove weapon if taken).

Edit 2: was exhausted when writing previous edit. My previous mistake has been corrected.

Code: Select all

void() BackpackTouch =
{
	local	float	old, new;
	local	float	acount;

	local	float	taken_shells, taken_nails, taken_rockets, taken_cells, taken_item;

	if (other.classname != "player")
		return;
	if (other.health <= 0)
		return;

	acount = 0;


	other.ammo_shells = (other.ammo_shells + self.ammo_shells);
	taken_shells = self.ammo_shells;
	if (other.ammo_shells > 100)
	{
		self.ammo_shells = other.ammo_shells - 100;
		other.ammo_shells = 100;
	}
	else
		self.ammo_shells = 0;
	taken_shells = taken_shells - self.ammo_shells;


	other.ammo_nails = (other.ammo_nails + self.ammo_nails);
	taken_nails = self.ammo_nails;
	if (other.ammo_nails > 200)
	{
		self.ammo_nails = other.ammo_nails - 200;
		other.ammo_nails = 200;
	}
	else
		self.ammo_nails = 0;
	taken_nails = taken_nails - self.ammo_nails;


	other.ammo_rockets = (other.ammo_rockets + self.ammo_rockets);
	taken_rockets = self.ammo_rockets;
	if (other.ammo_rockets > 100)
	{
		self.ammo_rockets = other.ammo_rockets - 100;
		other.ammo_rockets = 100;
	}
	else
		self.ammo_rockets = 0;
	taken_rockets = taken_rockets - self.ammo_rockets;


	other.ammo_cells = (other.ammo_cells + self.ammo_cells);
	taken_cells = self.ammo_cells;
	if (other.ammo_cells > 100)
	{
		self.ammo_cells = other.ammo_cells - 100;
		other.ammo_cells = 100;
	}
	else
		self.ammo_cells = 0;
	taken_cells = taken_cells - self.ammo_cells;


	old = other.weapon;
	if (self.items)
	{
		if ((other.items & self.items) == 0)
		{
			new = self.items;
			other.items = (other.items | new);
			self.items = 0;
			taken_item = 3;
		}
	}	

	if (!new)
		new = other.weapon;


	//don't do the screen flash or generate a message unless necessary
	//Don't bother players with messages, sound, or screenflash when they choose to stand
	//on a backpack.
	if ((taken_shells + taken_nails + taken_rockets + taken_cells + taken_item) > 2)
	{
		sprint (other, "You get ");

		if (taken_item)
		{
			acount = 1;

			sprint (other, "the ");
			sprint (other, self.netname);
		}
		if (taken_shells)
		{
			if (acount)
			sprint(other, ", ");
		acount = 1;
			s = ftos(taken_shells);
			sprint (other, s);
			sprint (other, " shells");

		}
		if (taken_nails)
		{
			if (acount)
			sprint(other, ", ");
		acount = 1;
			s = ftos(taken_nails);
			sprint (other, s);
			sprint (other, " nails");
		}
		if (taken_rockets)
		{
			if (acount)
			sprint(other, ", ");
		acount = 1;
			s = ftos(taken_rockets);
			sprint (other, s);
			sprint (other, " rockets");
		}
		if (taken_cells)
		{
			if (acount)
			sprint(other, ", ");
		acount = 1;
			s = ftos(taken_cells);
			sprint (other, s);
			sprint (other, " cells");
		}
		sprint (other, "\n");

		// backpack touch sound and screen effect
		sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM);
		stuffcmd (other, "bf\n");
	}

	// remove the backpack if it has no ammo
	if (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells))
		remove(self);


	// change to the weapon if it was taken.
	self = other;
	if (!deathmatch)
		self.weapon = new;
	else
		Deathmatch_Weapon (old, new);

	if (old != self.weapon)
		W_SetCurrentAmmo ();
	else
	{
		//mini W_SetCurrentAmmo()
		//previous weapon and current weapon are the same.
		//Just update the HUD; don't do weapon switch pause.
		if (self.weapon == IT_SHOTGUN)
			self.currentammo = self.ammo_shells;
		else if (self.weapon == IT_SUPER_SHOTGUN)
			self.currentammo = self.ammo_shells;
		else if (self.weapon == IT_NAILGUN)
			self.currentammo = self.ammo_nails;
		else if (self.weapon == IT_SUPER_NAILGUN)
			self.currentammo = self.ammo_nails;
		else if (self.weapon == IT_GRENADE_LAUNCHER)
			self.currentammo = self.ammo_rockets;
		else if (self.weapon == IT_ROCKET_LAUNCHER)
			self.currentammo = self.ammo_rockets;
		else if (self.weapon == IT_LIGHTNING)
			self.currentammo = self.ammo_cells;
		else
			self.currentammo = 0;
	}
};
Last edited by lurker on Sat Jun 20, 2009 11:29 pm, edited 1 time in total.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Post by Cobalt »

Hey, thanks!

I also worked on some new code of my own but had problems with
the leftover ammo never dispensing. So I commented it all out and tried your code, which is a huge step forward.

Its really nice, but one thing that needs fixing is if you stand over the backpack and are firing the shotgun with 100 shells in your gun, you hear the guncock from the pack and you take a shell from the pack every time. Im thinking self.attack_finished needs to be put in there to prevent this from happening.

Another thing is that when you get craning with alot of frags, you wind up with many of these 'lesser' value ammo packs with shells laying around. So Im thinking to shorten the time do remove(self) happens quicker when more than 1 touch happens.

Also I noticed that sometimes you can touch a pack and get its (extra) shells, and one time I got the grenade launcher , with no grenades....which is kool, but I want to check the code again and make sure its giving up the weapon and deducting it from the pack so when its touched again, the weapon is not given again. Maybe you can tell me if that is working?
lurker
Posts: 2
Joined: Sat Jul 29, 2006 8:22 pm

Post by lurker »

I was exhausted when adding the weapon check and I think I introduced a bug. I have rewritten the whole section while addressing another of your concerns. Due to its length, the updated version appears at the end of my previous post.

The weapon pickup/guncock sound no longer plays if the player is standing on a backpack while attacking. The player's ammo count continues to be updated as long as he or she stands on the backpack. I chose this in favor of setting .attack_time in case two players were both attempting to grab a backpack at the same time: if there is spare ammo, it wouldn't be fair to prevent the second player from grabbing it since the backpack is still clearly present.

In standard progs.dat, grabbing backpacks interrupts the player's current attack. I modified this to only interrupt the attack if the player's weapon has been changed by the backpack.


I agree: this sort of backpack_touch does leave too many backpacks in play. The quickest way to address this is to lower the timer in the following line of DropBackpack() :

Code: Select all

item.nextthink = time + 120;	// remove after 2 minutes
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Post by Cobalt »

Looks interesting, gonna try it. I noticed you got rid of stemp = self and stemp = self. I never knew why they did that in much of the original code, it seems precautionary. All the code I ever wrote, I never needed to do that. The timer deduction for remove(self) is clever...I am using a .specific to count up to 4 each time the pack is touched, then it removes self. I may add something that detects if the pack contains only shells at spawn, make the timer about half or less than the default, to help lessen the number of packs laying around on the map.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Setting the time according the content looks a good idea IMO. Backpacks holding only a few shells last 20 or 30 seconds, but one containing a rocket launcher deserves to survive for 2 minutes.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Post by Cobalt »

Ok, finally got your code working with mine ! I used your ammo calculations as they seem to be flawless. I redid the 'you get' sprint, and of course the bf and sound so that it happens only when we have 'taken' something. Still got a bug with the comma popping up when you get just nails, rockets or cells, but it ought to be easy to fix. I took your advise and shortened the nextthink time by 15 if all we have are shells. Lot less packs floating around now.

Its amazing how one change like this alters gameplay for the bots. They have a tendency to hang around the ammo packs now, which seems to make gameplay a little more smoother and fun. It felt like a real CTF game.
Post Reply