Go Back   EQEmulator Home > EQEmulator Forums > Quests > Quests::Q&A

Quests::Q&A This is the quest support section

Reply
 
Thread Tools Display Modes
  #1  
Old 02-14-2013, 08:32 AM
Loxo
Fire Beetle
 
Join Date: Jun 2012
Posts: 5
Default $mob->DoKnockback(?);

Code:
XS(XS_Mob_DoKnockback); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_DoKnockback)
{
	dXSARGS;
	if (items != 4)
		Perl_croak(aTHX_ "Usage: Mob::DoKnockback(THIS, caster, pushback, pushup)");
	{
		Mob *		THIS;
		Mob *       caster;
		uint32		pushback = (uint16)SvUV(ST(2));
		uint32		pushup = (uint16)SvUV(ST(2));

		if (sv_derived_from(ST(0), "Mob")) {
			IV tmp = SvIV((SV*)SvRV(ST(0)));
			THIS = INT2PTR(Mob *,tmp);
		}
		else
			Perl_croak(aTHX_ "THIS is not of type Mob");
		if(THIS == NULL)
			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");

		if (sv_derived_from(ST(1), "Mob")) {
			IV tmp = SvIV((SV*)SvRV(ST(1)));
			caster = INT2PTR(Mob *,tmp);
		}
		else
			Perl_croak(aTHX_ "caster is not of type Mob");
		if(caster == NULL)
			Perl_croak(aTHX_ "caster is NULL, avoiding crash.");

		THIS->DoKnockback(caster, pushback, pushup);
	}
	XSRETURN_EMPTY;
}
I'm having trouble finding what will work as a variable for "caster." I have tried numerous variations on $mobid, $charid, $userid, just plain $mob/$npc/$client; but they all end up giving me an undefined report:
Code:
[02.14. - 06:09:54] Script error: qst1002524::EVENT_TIMER - Perl runtime error: Can't call method "DoKnockback" on an undefined value at quests/wood/northwind.pl line 45 (or 56).
This is the script I'm trying to use the object in, currently unsuccessfully using $character = $ent to try to specify, though I have tried various other mob/npc/client global variables as I'm not sure from the perl_mob.cpp who "caster" is supposed to be exactly:
Code:
sub EVENT_SPAWN
{
my $randomgail = (int(rand(1800)) + int(rand(1200)));
my $randomgust = (int(rand(1200)) + int(rand(600)));
my $randombreeze = (int(rand(600)) + int(rand(300)));
	quest::settimer("gail",31);
	quest::settimer("gust",53);
	quest::settimer("breeze",71);
}

sub EVENT_TIMER
{
my $delaywind = (int(rand(10)) + 1);
	if ($timer eq "breeze")
	{
		quest::settimer("windone",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "gust")
	{
		quest::settimer("windtwo",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "gail")
	{
		quest::settimer("windthree",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "windone")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
	                    $npc->CastSpell(697, $userid);
						quest::ze(10, "A gentle breeze passes through.");
						quest::stoptimer("windone");
	            }
	}
	elsif ($timer eq "windtwo")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					$character = $ent;
	                    $mob->DoKnockback($character, 15, 0);
						quest::ze(10, "A gust from the North sweeps through.");
						quest::stoptimer("windtwo");
	            }
	}
	elsif ($timer eq "windthree")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					$character = $ent;
	                    $mob->DoKnockback($character, 1, 0);
						quest::stoptimer("windthree");
						quest::settimerMS("constwind", 500);
						quest::ze(10, "The wind howls...");
	            }
	}
	elsif ($timer eq "constwind")
	{
	my $randomstopwind = int(rand(10));
		if ($randomstopwind < 9)
		{
			my @clientlist = $entity_list->GetClientList();
					foreach $ent (@clientlist)
					{
					$character = $ent;
						$mob->DoKnockback($character, 1, 0);
					}
		}
		else
		{
			quest::stoptimer("constwind");
			quest::ze(10, "The wind dies down");
		}
	}
	else
	{
	}
}
I've had success in other scripts defining a $variable = ($client) outside of the EVENT_TIMER so I could specify which client I wanted inside the timer, but with the $entity_list I don't think that will work, as I want these small knockbacks to hit each player in the zone.
Reply With Quote
  #2  
Old 02-14-2013, 09:08 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Here is the actual function from mob.cpp:

Code:
void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup)
{
	if(IsClient())
	{
		CastToClient()->SetKnockBackExemption(true);

		EQApplicationPacket* outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
		PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;

		double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY());
		look_heading /= 256;
		look_heading *= 360;
		if(look_heading > 360)
			look_heading -= 360;

		//x and y are crossed mkay
		double new_x = pushback * sin(double(look_heading * 3.141592 / 180.0));
		double new_y = pushback * cos(double(look_heading * 3.141592 / 180.0));

		spu->spawn_id	= GetID();
		spu->x_pos		= FloatToEQ19(GetX());
		spu->y_pos		= FloatToEQ19(GetY());
		spu->z_pos		= FloatToEQ19(GetZ());
		spu->delta_x	= NewFloatToEQ13(new_x);
		spu->delta_y	= NewFloatToEQ13(new_y);
		spu->delta_z	= NewFloatToEQ13(pushup);
		spu->heading	= FloatToEQ19(GetHeading());
		spu->padding0002	=0;
		spu->padding0006	=7;
		spu->padding0014	=0x7f;
		spu->padding0018	=0x5df27;
		spu->animation = 0;
		spu->delta_heading = NewFloatToEQ13(0);
		outapp_push->priority = 6;
		entity_list.QueueClients(this, outapp_push, true);
		CastToClient()->FastQueuePacket(&outapp_push);
	}
}
As you can see, "THIS" needs to be a client or it won't do anything. A bit odd since it was added as a mob perl object. Anyway, I think the use would be like this:

$client->DoKnockback($npc, x, x);

I don't know what the pushback and pushup should be set to, so I just used "x" in the example, but I imagine you can figure that part out with some simple testing once you have it basically working.

I would suggest testing it with EVENT_SAY first to make sure you are using it properly. If you want to use it in EVENT_TIMER, then you are going to have a bit of an issue. The $npc variable is exported to EVENT_TIMER for npc scripts, but since it is a timer, there is no actual client triggering it, so there is no $client variable exported to EVENT_TIMER. If you want to use a client as a pointer like that from EVENT_TIMER, my best suggestion would be to save the entity ID of the client you want to knockback from one of the other sub events such as EVENT_COMBAT or whatever. Then, in the EVENT_TIMER, you can get the client by the entity ID and validate that you were able to get the client before trying to use it as a pointer. Otherwise, if you try to just save the client as a variable outside the sub and the client zoned or died or whatever, it can cause a zone crash if you don't validate it still exists first.

Alternatively, you can just get the NPCs current target and check if it is a client, then knock it back. Or, you can do an entity list search through the client list and use knockback on any or all of the clients in the zone or within X distance from the NPC.

The possibilities are endless.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #3  
Old 02-14-2013, 07:31 PM
Loxo
Fire Beetle
 
Join Date: Jun 2012
Posts: 5
Default

Ok thank you for clearing that up and for the tip on needing to validate, really I would have had assumed I had a corrupt perl file or something.

I think I already have a simple script of how to get $client into EVENT_TIMER as part of a climber, maybe it can be useful as a reference.
Reply With Quote
  #4  
Old 02-15-2013, 08:19 AM
Loxo
Fire Beetle
 
Join Date: Jun 2012
Posts: 5
Default

a working version of the first script for comparison:
Code:
##northwind.pl
sub EVENT_SPAWN
{
my $randomgail = (int(rand(1800)) + 300);
my $randomgust = (int(rand(1200)) + 300);
my $randombreeze = (int(rand(600)) + 300);
	quest::settimer("gail",$randomgail);
	quest::settimer("gust",$randomgust);
	quest::settimer("breeze", $randombreeze);
}

sub EVENT_TIMER
{
my $delaywind = (int(rand(10)) + 1);
	if ($timer eq "breeze")
	{
		quest::settimer("windone",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "gust")
	{
		quest::settimer("windtwo",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "gail")
	{
		quest::settimer("windthree",$delaywind);
		quest::ze(10, "The tree tops begin to rustle...");
	}
	elsif ($timer eq "windone")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					if($npc->CheckLoS($ent))
					{
	                    $npc->SignalClient($ent, 11111);
						quest::ze(10, "A gentle breeze passes through.");
						quest::stoptimer("windone");
					}
					else
					{
					}
	            }
	}
	elsif ($timer eq "windtwo")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					if($npc->CheckLoS($ent))
					{
						$ent->DoKnockback($npc, 2, 0);
						quest::ze(10, "A gust from the North sweeps through.");
						quest::stoptimer("windtwo");
					}
					else
					{
					}
	            }
	}
	elsif ($timer eq "windthree")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					if($npc->CheckLoS($ent))
					{
	                    $ent->DoKnockback($npc, 1, 0);
						quest::stoptimer("windthree");
						quest::settimerMS("constwind", 150);
						quest::ze(10, "The wind howls...");
					}
					else
					{
					}
	            }
	}
	elsif ($timer eq "constwind")
	{
	my $randomstopwind = int(rand(10));
		if ($randomstopwind < 9)
		{
			my @clientlist = $entity_list->GetClientList();
					foreach $ent (@clientlist)
					{
						if($npc->CheckLoS($ent))
						{
							$ent->DoKnockback($npc, 0.20, 0);
						}
						else
						{
						}
					}
		}
		else
		{
			quest::stoptimer("constwind");
			quest::ze(10, "The wind dies down");
		}
	}
	else
	{
	}
}
Just to verify, will...
Code:
elsif ($timer eq "windtwo")
	{
		my @clientlist = $entity_list->GetClientList();
	            foreach $ent (@clientlist)
	            {
					if($npc->CheckLoS($ent))
					{
						$ent->DoKnockback($npc, 2, 0);
						quest::ze(10, "A gust from the North sweeps through.");
						quest::stoptimer("windtwo");
					}
					else
					{
					}
	            }
	}
...work for validating a player is still in the zone, as it checks the list then checks LoS (which would report back a 0 for any player on the list no longer present?) for any client on the list each time the timers go off?
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 01:45 AM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3