PDA

View Full Version : random loot, the easy way


c0ncrete
01-27-2013, 10:04 AM
i finally got around to using the new global_npc.pl script
it makes loot randomization so much easier...

this small snippet
- doesn't make changes to default loot chances
- only gives npcs with existing loot tables are given an extra item
(so untargetable, invisible npcs won't turn into floating weapons)
- verifies itemid is valid via temporary itemlink creation

EQEmu\quests\templates\global_npc.pl

sub EVENT_SPAWN {
return unless $npc->GetLoottableID();
my ($tested, $itemid);
do {
$itemid = ( int rand 131474 ) + 1001;
$tested = quest::varlink($itemid);
} while $tested =~ /INVALID/;
$npc->AddItem($itemid);
}


- can easily be expanded upon

examples:
filter specific items from roll
destroy extra item under certain conditions
roll for chance at extra item
additional random items

NOTE: you'll want to change the number from 131474 to whatever the highest itemid is in your items table and subtract 1001 (or your lowest itemid) from that number.

Drajor
01-27-2013, 11:21 AM
Hah that is awesome!

c0ncrete
01-27-2013, 12:01 PM
glad you like it. i've added all sorts of bells and whistles to this on my test server. trivial encounter check, filtering based on item usability by encounter participants and/or an arbitrary never-drop list, minimum base chance and additional chance scaled by npc level, etc.

my previous method required that you modify every existing npc quest script you wanted to apply random loot to. needless to say, the recent addition of global_npc simplified things a great deal. thanks, akkadius!

c0ncrete
01-28-2013, 06:44 PM
using something very close to this to verify that the npc can equip the item in question before the call to $npc->AddItem(). read: no more augfists, yay!

it can be modified for use as an additional validation step while picking the random item (filtering out items that are not usable by an participant in the encounter, for example).

this was fun! (no sarcasm)


use 5.012;
no strict 'vars';

use constant {
MIN_ITEMID => 1001,
MAX_ITEMID => 132475,
};

# class patterns used to build regular expressions for bit values
my $war = '1|war(?:rior)?';
my $clr = '2|cl(?:eric|r)';
my $pal = '3|pal(?:adin)';
my $rng = '4|r(?:anger|ng)';
my $shd = '5|sh(?:adowknight|d)';
my $dru = '6|dru(?:id)?';
my $mnk = '7|m(?:o)?nk';
my $brd = '8|b(?:a)?rd';
my $rog = '9|rog(?:ue)?';
my $shm = '10|sh(?:aman|m)';
my $nec = '11|nec(?:romancer)?';
my $wiz = '12|wiz(?:ard)?';
my $mag = '13|mag(?:ician)?';
my $enc = '14|enc(?:hanter)?';
my $bst = '15|b(?:eastlord|st)';
my $ber = '16|ber(?:serker)?';

# race patterns used to build regular expressions for bit values
my $hum = '1|hum(?:an)?';
my $bar = '2|bar(?:barian)?';
my $eru = '3|eru(?:dite)?';
my $elf = '4|(?:wood )?elf';
my $hie = '5|hi(?:gh elf|e)';
my $def = '6|d(?:ark elf|ef)';
my $hel = '7|h(?:alf-elf|el)';
my $dwf = '8|dw(?:ar)?f';
my $trl = '9|tr(?:ol)?l';
my $ogr = '10|ogr(?:e)?';
my $hlf = '11|h(?:alfling|lf)';
my $gnm = '12|gn(?:ome|m)';
my $iks = '128|iks(?:ar)?';
my $vah = '130|vah(?: shir)?';
my $frg = '330|fr(?:oglok|g)';
my $drk = '522|dr(?:akkin|k)';

# get bit value for any playable race/class you pass it
# works with numeric values, abbreviations, and full spellings
sub GetBitFor {
my $bit = {
1 => qr/^$war|$hum$/i,
2 => qr/^$clr|$bar$/i,
4 => qr/^$pal|$eru$/i,
8 => qr/^$rng|$elf$/i,
16 => qr/^$shd|$hie$/i,
32 => qr/^$dru|$def$/i,
64 => qr/^$mnk|$hel$/i,
128 => qr/^$brd|$dwf$/i,
256 => qr/^$rog|$trl$/i,
512 => qr/^$shm|$ogr$/i,
1024 => qr/^$nec|$hlf$/i,
2048 => qr/^$wiz|$gnm$/i,
4096 => qr/^$mag|$iks$/i,
8192 => qr/^$enc|$vah$/i,
16384 => qr/^$bst|$frg$/i,
32768 => qr/^$ber|$drk$/i,
};
my $for = shift;
while ( my ( $key, $val ) = each %$bit ) {
return $key if $for ~~ $val;
}
return 0;
}

sub CanEquip {

# itemid to check
my $itemid = shift;

# coderef for $npc->GetItemStat()
my $GISref = sub { $npc->GetItemStat(@_) };

# list of tests we want to do, in order of importance
# 1. is itemid within valid range?
# 2. is itemid tied to a valid item (some id number gaps exist)?
# 3. is itemid able to be equipped at all?
# 4. is itemid not an aug?
# 5. is itemid not deity specific?
# 6. is itemid's required level less than or equal to my level?
# 7. is itemid's class restriction compatible with my class?
# 8. is itemid's race restriction compatible with my class?
my $test = [
sub { shift ~~ [ MIN_ITEMID .. MAX_ITEMID ] },
sub { quest::varlink(shift) !~ /INVALID/ },
sub { $GISref->( shift, "slots" ) },
sub { !$GISref->( shift, "augtype" ) },
sub { !$GISref->( shift, "deity" ) },
sub { $GISref->( shift, "reqlevel" ) <= $mlevel },
sub { $GISref->( shift, "classes" ) & GetBitFor( $npc->GetClass() ) },
sub { $GISref->( shift, "races" ) & GetBitFor( $npc->GetRace() ) },
];

# run through tests, stopping at first failure
foreach (@$test) {
return 0 unless $_->($itemid);
}

# passed all tests
return 1;
}

jdoran
01-29-2013, 04:21 PM
I've never played on such a server, so I wonder why one would ever fight tough mobs when there are rats in the newbie area potentially dropping end-game gear. (I'm not saying that is necessarily a bad thing, I just wonder how things would play out)

c0ncrete
01-29-2013, 05:39 PM
some people just like the idea of random loot. the initial snippet was to give those people something to play with. technically speaking, most servers don't actually employ randomization, they simply scramble the drops in the database. they're still static that way. the misnomer is a pet peeve of mine.

as for your concerns, i had them as well. i'm working on implementing ways to determine if the encounter was trivial and remove the extra item. i'm also looking into filtering out loot based on the required level of the item. so the npc won't get something outside of its level range.

my objective on my personal server is for there to only be a very small chance of the extra item, which scales up a tiny bit with npc difficulty (starting at level 11), so the extra item won't be something that is a common occurrence to begin with.

finally, i'm considering giving the npcs in these rare instances abnormal abilities, so as to make things more of a challenge, given the extra bounty.

the devil is in the details ... :)

c0ncrete
01-29-2013, 07:53 PM
yow. the original won't work as posted because of the way quest::varlink() works. if you call it without a client as the initiator, the zone will crash. i was testing the functionality via EVENT_SAY, so it worked without issue.

compiling a minor source modification to see if it takes care of the crash without altering the functionality of quest::varlink() now. it'll just return an empty string if there is no client initiator, but only after it validates the itemid passed to it.

c0ncrete
01-29-2013, 09:24 PM
applying the source change found here (http://www.eqemulator.org/forums/showthread.php?t=36375) and changing

while $tested =~ /INVALID/

to

while $tested =~ /INVALID ITEM/

resolved the issue.

jdoran
01-30-2013, 12:48 PM
I can see the attraction of an occasional random item (random rewards are more effective psychologically, think Pavlov did some work on this). I'd actually like to play on such a server to check it out.

If I were doing such a thing, I would create a singleton on the server that built a list of items by level. Then have it select something from a requested range through a new Perl call. (I know you weren't asking for suggestions, but I thought I'd throw that out there. It might inspire someone).

Edit: I just realized that this would be a good candidate for the shared memory region. No need to replicate the table across zones.

c0ncrete
01-30-2013, 01:04 PM
no worries. suggestions are always appreciated. i've got plenty of ideas, just not enough skill to do anything other than hack them together with perl and my limited knowledge of c++.