PDA

View Full Version : Quest flags for 5.2


mollymillions
02-07-2004, 12:19 AM
If you cannot compile code and cant get quest flags working, you can use this plugin to allow for zone wide flags (the flags should be restored if the client re-enters the zone)
Pugin.plsub get_flag { return $flags{$_[0]}; }
sub set_flag { $flags{$_[0]} = "$_[1]"; }
Use plugin::set_flag("flagname","flagvalue") to set a flag and plugin::get_flag("flagname") to return a flag. The flagnames and flagvalues can be anything.

This will work with any version of EQEmu that has Perl plugins enabled.

Eglin
02-07-2004, 12:46 AM
No offense, but this is pointless. The only need for a more formal flag system would be in cases where information needs to be passed back into the underlying game engine (I don't know how planar access works, but I have been working under the understanding that this is exactly what the flags were for). Perl alone is incapable of doing this. You either have to write some code to talk to the db or write some xs.

Scorpious2k
02-07-2004, 02:10 AM
The code already exists and will be out soon in cvs.

http://www.eqemulator.net/forums/viewtopic.php?t=12773

Not only does it replace flags, but it allows for actual values to be passed and not just a true/false boolean values. It also has the advantage of setting the scope of availability to determine which NPCs get it, what zone and what players. Finally, it lets you set the amount of time the variable is available, after which it goes POOF.

I am surprised that this hasn't generated more excitement. The possibilities it opens up are incredible.

You can now make sure all 4 warders are dead in sleeper before spawning the sleeper. Want to know by whom and when sleeper was killed? Write a global variable.

Wandering mobs? This makes it possible for them to know where each other are (variable written at event waypoint). Want a mob to behave a certain way, even in another zone, based on what was done with another npc?

Then there are the adventures... you have 30 minutes to start. Guess what? Set the variable to a 30 min duration and if it isn't there they missed the window of opportunity....

And I am working on a little addition which will make it possible, among many other things, for mobs to have a conversation among themselves.

Eglin
02-07-2004, 02:28 AM
That sounds cool. While the sleeper thing, the waypoint thing, the conversation thing, et. al. could already be easily done w/ the existing perl quest routines, the ability to transparently store values into the db is very nice.

Scorpious2k
02-07-2004, 02:55 AM
the sleeper thing, the waypoint thing, the conversation thing, et. al. could already be easily done w/ the existing perl quest routines

The problem with the current quest system is that each time an event is triggered, the event starts out new. Having no information about anything that has come before.

This has produced the classic and tiresome quests of taking something to someone, who gives you something else to take to someone else who then gives you something else... and on and on. Why? Because giving the item to the npc causes an event item. The event item can then check the item he has been given and react accordingly.

Now its possible to remember what came before. Instead of checking the item given, check the variable saved or sent by another npc. Now, an npc can tell a player to "go talk to Dogmouth", targlobal a value to Dogmouth and when the player gets there, Dogmouth looks at the variable and says "I've been expecting you, Eglin, take these perls [pun intended] to MollyMillions". If the variable isn't there, the player isn't on the quest and Dogmouth can say "Buzz off"

As far as the sleeper thing.. the closest I've seen possible is to spawn one at a time. Kill 1, spawn 2, kill 2 spawn 3, kill 3 spawn 4, kill 4 spawn sleeper... Now all four can be spawned in the zone, each checks and updates the global variables they share. The last one to die spawns sleeper.

The waypoint and conversation things... I have no clue how you would be able to do. Maybe you could post some examples.

the ability to transparently store values into the db is very nice.

And in many ways, it is a side benefit.

smogo
02-07-2004, 03:01 AM
I am surprised that this hasn't generated more excitement. The possibilities it opens up are incredible
It's not advisable to post when you're excited about a topic.

-This- is the only exception i make. :D

Scorpious2k
02-07-2004, 04:12 AM
It's not advisable to post when you're excited about a topic.

It's more a case of surprise. I see people complain about spending their time doing nothing but killing mobs. The majority of quests are lame.

Now we can do something about it. It's the quest writers and serverops that should be excited.

smogo
02-07-2004, 04:21 AM
i got the latest cvs, the source reflects changes, and should now allow targlobal, ...

i found no sql update in the repository. Should we excpect one, or did i miss something in the posts ? Tark says about quest_globals.sql.

Scorpious2k
02-07-2004, 04:26 AM
Should have been included. I'll talk to Shawn.

Here is what it says:

DROP TABLE IF EXISTS quest_globals;
CREATE TABLE quest_globals (
id int(11) NOT NULL auto_increment,
charid int(11) NOT NULL default 0,
npcid int(11) NOT NULL default 0,
zoneid int(11) NOT NULL default 0,
name varchar(65) NOT NULL,
value varchar(65) NOT NULL default "?",
expdate int(11) NOT NULL default 0,
PRIMARY KEY (id),
UNIQUE KEY qname (name,charid,npcid,zoneid)
) TYPE=MyISAM;

alter table npc_types add qglobal int(2) unsigned default '0';

smogo
02-07-2004, 05:18 AM
thx

Eglin
02-07-2004, 07:04 AM
The problem with the current quest system is that each time an event is triggered, the event starts out new. Having no information about anything that has come before.Although I have hinted about using DBI to connect to the db, I did not provide a default method for permanent data storage. There is nothing preventing storage of state between events, you just have to take care of serializing it on your own. I maintain that each

As far as the sleeper thing.. the closest I've seen possible is to spawn one at a time. Kill 1, spawn 2, kill 2 spawn 3, kill 3 spawn 4, kill 4 spawn sleeper... Now all four can be spawned in the zone, each checks and updates the global variables they share. The last one to die spawns sleeper.I maintain that the only new functionality is related to data storage and variable lifespan. In this case, data storage is probably unnecessary (since the spawns all respawn at zone reboot, don't they?). You add something like "sub event_spawn { $isalive = true; }" in each one of the spawn's scripts. You also add to each something like "sub event_death {$isalive = false; if(!$isalive && !$qstxxx1::isalive && !$qstxxx2::isalive ... ) quest::spawn(sleeper); }" You'll have to forgive me for not remembering the correct syntax of some of the commands. I don't have much practice writing quests.

The waypoint and conversation things... I have no clue how you would be able to do. Maybe you could post some examples.You do them in the same way as the sleeper globals, above. I am wondering if you're having the same confusion that Molly had regarding packages and global variables. Variables declared with global scope will retain their state, much the way that static variables work within function scope in C. You can save an arbitrary amount of state info, there just wasn't a default method for data serialization (db storage, tied hashes, whatever). If you want to have a variable remember a value between events, just declare it w/o "my" or "local" To access it outside of the current package, just prefix the name with something like qstxxxx where xxxx is the npcid in question. Granted, such vars don't persist across hard zone reboots, but it should still be sufficient for stuff like spawning creatures, moving them around, making them talk to each other, whatever. Obviously, these global vars are only zone-wide - another reason that serializing to the db is handy.

the ability to transparently store values into the db is very nice.
And in many ways, it is a side benefit.Oh? That is by far the part that I think is the coolest addition. I've hinted many times about caching a DBI connection in a global var. An XS interface to Quagmire's excellent db classes is even better.

Eglin
02-07-2004, 09:15 AM
alright... nobody asked my opinion on the matter, but I just looked at the code for the first time and I have to say that I think it has some issues. The most important is that you're making two database queries EVERY time the event loop runs. This is insanity, man. Think of how often the event loop runs: every time any mob hits a waypoint, every time any mob spawns, every time any mob dies, every time any mob gets hailed, etc etc... This bad boy is getting called _constantly_. Also, consider the size of the table you're creating... it is going to grow _very_ fast. You do _not_ want to search through a table of _every_ variable stored for _every_ character ever created _every_ time the event loop runs. The DB call is going to block the entire thread. You just can't do it willy-nilly. It doesn't make sense.

Choosing to key your data around values that you have to query seperately doesn't make any sense, either. Why would you query for a charid? You have enough data avaliable to generate a unique key, so why tolerate the expense of the charid queries?

OK. Sorry for ranting about your work. You actually wrote some code, and I'm pragmatic enough to know that 1 diff is worth 1000 forum posts. OTOH, it is 100% clear to me that caching a global DBI connection for variable storage and only using it when variables actually need to be queried/written is going to be _WAY_ more efficient than making a minimum of 2 queries _EVERY_ single time the event loop runs.

Scorpious2k
02-07-2004, 09:21 AM
alright... nobody asked my opinion on the matter, but I just looked at the code for the first time

Read it again.

This time pay attention to the fact it will ONLY do it if the NPC has been specifically flagged in the npc_types to use this feature.

Eglin
02-07-2004, 10:01 AM
pay attention to the fact it will ONLY do it if the NPC has been specifically flagged in the npc_types to use this feature. You are correct to assume that I misinterpreted the semantics of that check. But.... what if I want to create a situation where, say, a pc is flagged for killing a townie and the default quest would check a given flag under some circumstances? Which npc do we flag in the db? How do I set it up so that everyone in the plane of love is extra nice if you've been to the plane of destiny before? How, in general, do we know from a script whether or not we have access to player flags, if not all scripts would? Granted, the check may prevent unnecessary db access, but it opens up a whole new set of problems instead (or am I still missing something?).

Scorpious2k
02-07-2004, 11:11 AM
But.... what if I want to create a situation where, say, a pc is flagged for killing a townie and the default quest would check a given flag under some circumstances?

The appropriate thing to use in this case would be factions. IMO, this feature has no place in in a default quest.

How do I set it up so that everyone in the plane of love is extra nice if you've been to the plane of destiny before?

Factions again.

How, in general, do we know from a script whether or not we have access to player flags, if not all scripts would?

It's a matter of design. If you need it, then you set the flag. From the script, you might put a comment in if you are worried about knowing whether or not the feature is used (and can't figure it out when you see the commands used).

Granted, the check may prevent unnecessary db access, but it opens up a whole new set of problems instead (or am I still missing something?).

The only problem for some may be that thought and planning has to be done.

Eglin
02-07-2004, 12:05 PM
But.... what if I want to create a situation where, say, a pc is flagged for killing a townie and the default quest would check a given flag under some circumstances?The appropriate thing to use in this case would be factions. IMO, this feature has no place in in a default quest.Factions have a whole lot of baggage, though. For one thing, setting faction affects the way that a mob cons to an individual, which may be an unwanted side-effect.

How do I set it up so that everyone in the plane of love is extra nice if you've been to the plane of destiny before?
Factions again.Again, I don't think so. In the generic example of "be extra nice" you don't see the full ramifications of confusing faction with an arbitrary tag, so I will present another example. What if I want to have a quest that makes a victor into, say, the evil taxman of qeynos. The evil taxman can tell any npc to fork over some cash and they have to comply. The taxman can still kill denizens (to the detriment of his finances, perhaps) or kill their enemies to change his faction with them, although that won't necessarily have any bearing on his status as the evil taxman. One has nothing to do with the other. You say above that such features have no place in default quests, but I beg to differ. Regardless of how you implement it, it is going to require that a large number of npcs have access to the db vars which in turn means that you are going to be making all of those db queries every time you enter the event loop which is, like I said above, unacceptable.

How, in general, do we know from a script whether or not we have access to player flags, if not all scripts would?
It's a matter of design. That is exactly why I thought it deserved discussion. Why alter the semantics of scripting? Why should a command to check pc flags work from some scripts and not others?

If you need it, then you set the flag. From the script, you might put a comment in if you are worried about knowing whether or not the feature is used (and can't figure it out when you see the commands used). In my opinion, this is the narrow view. The alternative is to make a function for querying db vars avaliable to all npcs. The alternative allows us to maintain a consistant API. The alternative allows for more flexible scripting. The alternative runs faster.

Granted, the check may prevent unnecessary db access, but it opens up a whole new set of problems instead (or am I still missing something?).
The only problem for some may be that thought and planning has to be done.Wanna' elaborate on that for me, pal? I can only come up with two or three ways of interpreting that, and I don't particularly like the implications of any of them.

Trumpcard
02-07-2004, 12:16 PM
i found no sql update in the repository. Should we excpect one, or did i miss something in the posts ? Tark says about quest_globals.sql.

Its in there now, that was my fault.. I missed the changes to 'Release' when I updated...

Scorpious2k
02-07-2004, 12:30 PM
The only problem for some may be that thought and planning has to be done.Wanna' elaborate on that for me, pal? I can only come up with two or three ways of interpreting that, and I don't particularly like the implications of any of them.

What it means is that quest script writing is programming and should be approached as such. It should be thought out and planned.

I think you are taking this discussion personally. So I will move on to other things. The feature is there to be used. People are free to use it or not. They have the right to make something better to replace it. They even have the right to stand by and do nothing but criticize.

Eglin
02-07-2004, 12:55 PM
I think you are taking this discussion personally. So I will move on to other things. The feature is there to be used. People are free to use it or not. They have the right to make something better to replace it. They even have the right to stand by and do nothing but criticize.
To that, I will certainly concede. I will reiterate that I am a pragmatist, realizing that one diff is worth a thousand words and acknowledging that nobody asked my opinion. I'm not sure which of the above groups you're imagining me in, but while I don't allocate much time to working on my baby anymore (which is really just a tiny little portion of the eqemu whole), I do feel compelled to monitor its progress and offer guidance where it is needed.

mollymillions
02-07-2004, 04:02 PM
Thanks for doing the hard yards Scorp. Its good to see the carrot of server wide smarts, dangled by embedded Perl, being realised.

Eglin
02-07-2004, 11:13 PM
Thanks for doing the hard yards Scorp. Its good to see the carrot of server wide smarts, dangled by embedded Perl, being realised.I can't believe that nobody else sees this as a kludge.

smogo
02-08-2004, 11:40 AM
You guys talkin' a lot ; whatever the issues, hoppefully they will be addressed (is 1 or 2 d's and s's ?)

i've been trying to get this run, and :

there might be a thing in the setglobal code, where duration is arglist[3], and referd to as arglist[2]. This causes wrong expiry result Invalid duration for varname using default
Changed in parser.cpp : 1128
if (!database.RunQuery(query, MakeAnyLenString(&query,
"INSERT INTO quest_globals (charid,npcid,zoneid,name,value,expdate) VALUES (%i,%i,%i,'%s','%s',unix_timestamp(now())+%i)",
qgCharid,qgNpcid,qgZoneid,arglist[0],arglist[1],QG expdate(arglist[0],arglist[3])




there might be a thing with the insert, where i get
setglobal error inserting singer : DBcore::RunQuery: No Result
this does not seem to prevent insertion, but is confusing. Mybe this is an issue, i don't know what dbcore does exactly (and don't plan to check if possible). Any ideas ?

third, but not least :
next time the event is triggered, the variable is not defined (test with 'if defined $varname' returns false) and quest::say("value of varname is " . $varname); prints "value of varname is :". I'm in the tracing of this at the moment. Could it be $quest::varname, or $questNNNN::varname or even $main::varname or ... ?

BTW, when setting a variable, it is not yet available (requires to re-enter the event). Is it possible to modify the setglobal :
** edited **
"sub setglobal{push(@cmd_queue,{func=>'setglobal',args=>join(',',@_)}); eval "\$".$_[0]."= qw($_[1])";}"

would this work with the embedded parser (mean, it works in striaght perl, but ...) ?

Scorpious2k
02-08-2004, 02:32 PM
there might be a thing in the setglobal code, where duration is arglist[3], and referd to as arglist[2]. This causes wrong expiry result

You're right. It's fixed and will be right in the next release. And your fix was exactly right.

setglobal error inserting singer : DBcore::RunQuery: No Result

this does not seem to prevent insertion, but is confusing. Mybe this is an issue, i don't know what dbcore does exactly (and don't plan to check if possible). Any ideas ?

I haven't seen that one yet. Not sure what's going on...

next time the event is triggered, the variable is not defined (test with 'if defined $varname' returns false) and quest::say("value of varname is " . $varname); prints "value of varname is :". I'm in the tracing of this at the moment. Could it be $quest::varname, or $questNNNN::varname or even $main::varname or ... ?

If you want it to be $varname, you have to set it as

quest::setglobal("varname",...

Don't include the $ in the name in setglobal. Also make sure the name is a valid perl var name. You can confirm it by checking the db and making sure it is there. name should always be the same as the variable name without the leading $.

BTW, when setting a variable, it is not yet available (requires to re-enter the event).

You can always start out saying $varname = "myval"... use it thru the quest, and when you set it say

quest::setglobal("varname",$varname,...

That way it will be set to the current value (at the time the setglobal is done) for the next event. Maybe do this at the end of the event that initially creates it and each event where it is potentially changed.

smogo
02-08-2004, 02:46 PM
at the moment, declaring variables seems to work. But i dont get them in the context when the event is triggered. here is the status of the DB before bootup :



+----+--------+-------+--------+---------+--------------+------------+
| id | charid | npcid | zoneid | name | value | expdate |
+----+--------+-------+--------+---------+--------------+------------+
| 30 | 21 | 61 | 1 | tjob | story_teller | 2147483647 |
| 31 | 0 | 61 | 0 | job | singer | 2147483647 |
| 32 | 0 | 2222 | 0 | not_set | undefined_zz | 2147483647 |
| 37 | 0 | 61 | 1 | tjob | singer | 2147483647 |
+----+--------+-------+--------+---------+--------------+------------+


well, charid does not seem to work (thus 0 in char_id second line), but that should not prevent global to work.

There might be an issue with the qglobal member of the Mob class, as the code in parser.cpp / Parser::Event around line 313 is not entered. This member is set asynchrounsly ? Could you tell me what to check ?

At the moment, i never get a variable set, nor charid. =(

Scorpious2k
02-09-2004, 12:45 AM
at the moment, declaring variables seems to work. But i dont get them in the context when the event is triggered.

A thought occurred to me about this. You realize you have to give the npc permission to receive the global variable. The qglobal flag in npc_types has to be 1 or the npc will NOT get the variables no matter what is in the quest_globals table. Naturally, if you change the value in npc_types you have to restart the server for it to take affect.

well, charid does not seem to work (thus 0 in char_id second line)

charid can be 0, as can zone and npcid. 0 means "all". So if you set the options to allow this variable to be used by this npc for all players, the charid would be 0, npcid would be the npcid. Zero in zoneid means you are sharing this variable wroldwide.

Tark posted the list of options and what they mean in his message "coming soon" (http://www.eqemulator.net/forums/viewtopic.php?t=12773) where he announced it.

There might be an issue with the qglobal member of the Mob class, as the code in parser.cpp / Parser::Event around line 313 is not entered.

This is probably the qglobal flag I mentioned above.

At the moment, i never get a variable set, nor charid. =(

Hope there's something here to help.

smogo
02-09-2004, 01:49 AM
you where right about the qglobal flag in the DB, it was clear.

i set it, restarted, but sill it goes wrong. I trace it, and when entering Event() with :

if(npcid==61){
LogFile->write(EQEMuLog::Debug, "entre npc quest with id 61");
LogFile->write(EQEMuLog::Debug, "qglobal flag is :%d",
npcmob->GetQglobal());
}

i get

logs/qeynos.log:[Debug] qglobal flag is :0
logs/qeynos.log:[Debug] qglobal flag is :0
logs/qeynos.log:[Debug] qglobal flag is :0
logs/qeynos.log:[Debug] qglobal flag is :0

while

mysql> select * from quest_globals;
+----+--------+-------+--------+------+--------+------------+
| id | charid | npcid | zoneid | name | value | expdate |
+----+--------+-------+--------+------+--------+------------+
| 56 | 0 | 61 | 1 | tjob | singer | 2147483647 |
+----+--------+-------+--------+------+--------+------------+
1 row in set (0.00 sec)

mysql> select name, qglobal from npc_types where id=61;
+--------------+---------+
| name | qglobal |
+--------------+---------+
| Anehan_Treol | 1 |
+--------------+---------+
1 row in set (0.00 sec)


Also, $charid is still "". The (npcmob->GetQglobal()) must have returned false...[/quote]

Scorpious2k
02-09-2004, 02:16 AM
You shut down both te world server and all zone servers before restarting?

*looking at the code now*

smogo
02-09-2004, 02:31 AM
yes, i've been shutting down everything serverside, then start and login again. Takes a while :evil:

Scorpious2k
02-09-2004, 02:38 AM
yes, i've been shutting down everything serverside, then start and login again. Takes a while :evil:

I know... it gets frustrating.

smogo
02-09-2004, 02:42 AM
thanks for your time, anyway. i hope we can get a solution.

Scorpious2k
02-09-2004, 02:42 AM
You wouldn't happen to be getting this message on world or zone are you?

NPCs loaded - using old database format

smogo
02-09-2004, 03:04 AM
i do have this message.

Scorpious2k
02-09-2004, 03:10 AM
i do have this message.

Then the mystery is solved!

Some new things were added to the npc_types. The nice devs were good enough to take that into account and have 2 different ways to load the npcs. One of these ways, the old one, doesn't load any of the new things from the DB including qglobals. It seems you may be missing something that was added, Here is what a describe of npc_types should look like:

mysql> describe npc_types;
+-------------------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------------------+------+-----+---------+----------------+
| id | int(11) | | PRI | NULL | auto_increment |
| name | text | | | | |
| lastname | varchar(32) | | | | |
| level | tinyint(2) unsigned | | | 0 | |
| race | smallint(5) unsigned | | | 0 | |
| class | tinyint(2) unsigned | | | 0 | |
| bodytype | int(11) | YES | | NULL | |
| hp | int(11) | | | 0 | |
| gender | tinyint(2) unsigned | | | 0 | |
| texture | tinyint(2) unsigned | | | 0 | |
| helmtexture | tinyint(2) unsigned | | | 0 | |
| size | float | | | 0 | |
| hp_regen_rate | int(11) unsigned | | | 0 | |
| mana_regen_rate | int(11) unsigned | | | 0 | |
| loottable_id | int(11) unsigned | | | 0 | |
| merchant_id | int(11) unsigned | | | 0 | |
| npc_spells_id | int(11) unsigned | | | 0 | |
| npc_faction_id | int(11) | | | 0 | |
| mindmg | int(10) unsigned | | | 0 | |
| maxdmg | int(10) unsigned | | | 0 | |
| npcspecialattks | varchar(36) | | | | |
| banish | int(10) unsigned | | | 0 | |
| aggroradius | int(10) unsigned | | | 0 | |
| social | int(10) unsigned | | | 0 | |
| face | int(10) unsigned | | | 1 | |
| luclin_hairstyle | int(10) unsigned | | | 1 | |
| luclin_haircolor | int(10) unsigned | | | 1 | |
| luclin_eyecolor | int(10) unsigned | | | 1 | |
| luclin_beardcolor | int(10) unsigned | | | 1 | |
| fixedz | tinyint(2) unsigned | | | 0 | |
| d_meele_texture1 | int(10) unsigned | | | 0 | |
| d_meele_texture2 | int(10) unsigned | | | 0 | |
| walkspeed | float | | | 0 | |
| runspeed | float | | | 0 | |
| MR | smallint(5) | | | 0 | |
| CR | smallint(5) | | | 0 | |
| DR | smallint(5) | | | 0 | |
| FR | smallint(5) | | | 0 | |
| PR | smallint(5) | | | 0 | |
| ipc | tinyint(1) | | | 0 | |
| see_invis | tinyint(4) | | | 0 | |
| see_invis_undead | tinyint(4) | | | 1 | |
| qglobal | int(2) unsigned | YES | | 0 | |
+-------------------+----------------------+------+-----+---------+----------------+
43 rows in set (0.00 sec)

There is probably 1 or 2 thngs here that aren't in yours.

smogo
02-09-2004, 03:20 AM
yes, i was just checking that. then i sourced invis_update.sql, and well, the Mob::qglobal flag is set.

Yet i get a sig segv fault, but, as i twinked some things in the code, it's not too surprising... reverting to std code, and then 'let you know.

Scorpious2k
02-09-2004, 03:34 AM
You might want to get rid of the qglobal column and re-add it. It needs to be the last, just to be safe. Then set the ones you want again.

smogo
02-09-2004, 04:00 AM
YES ! It works. Changing col order ddin't matter, but i've done it anyway ;).

Thanks for your time :D

Now, Let It All Begin ... Tadam !!!

Scorpious2k
02-09-2004, 04:05 AM
YES ! It works

/cheers

Thanks for your time :D

No problems.

Now, Let It All Begin ... Tadam !!!

And now the fun begins....