View Single Post
  #1  
Old 04-05-2018, 04:04 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 719
Default dispatch tables, by example (Absor)

Was puttering around with ideas to clean up Absor (repetition & lots of if/elsif statements) in tutorialb in the wee hours, and this is the result. It's not the entirety of the script, but it is most of it. I left out the simpler bits and commented everything in order to make the logic easier to follow.

NOTE: This, and most other code, is orders of magnitude easier to read with proper syntax highlighting. Do yourself a favor and take advantage of it, even if you're using vim.

Code:
use constant {
    DAGGER => 9997,
        SHARPENED_DAGGER => 59950,
    SHORT_SWORD => 9998,
        SHARPENED_SHORT_SWORD => 59951,
    CLUB => 9999,
        POLISHED_CLUB => 59925,
    DULL_AXE => 55623,
        SHARPENED_AXE => 55623,
    CHUNK_OF_BRONZE => 54229,
        BRONZE_GLOOMINGDEEP_DAGGER => 54230,
        BRONZE_GLOOMINGDEEP_SWORD => 54231,
        BRONZE_GLOOMINGDEEP_MACE => 54232,
        BRONZE_GLOOMINGDEEP_AXE => 54233,
    CHUNK_OF_IRON => 59954,
        IRON_GLOOMINGDEEP_DAGGER => 54235,
        IRON_GLOOMINGDEEP_SWORD => 54236,
        IRON_GLOOMINGDEEP_MACE => 54237,
        IRON_GLOOMINGDEEP_AXE => 54238
};

# works like quest::summonitem(quest::chooserandom())
my $summon_from = sub {
    # accepts array or array ref as pool to select randomly from
    my @pool = ( ref $_[0] ) =~ /array/i ? @{ +shift } : @_;
    quest::summonitem( $pool[ int ( rand ( scalar @pool ) ) ] );
    # required because summonitem method returns nothing
    return 1;
};

# select item from pool based on player class and turn-in material value
my $forge_weapon = sub {
    my ( $class, $value ) = @_;
    # ! WARNING: see warning below about short circuiting ...
    # iterate over anon hash (dispatch table)
    while ( my ( $archetype, $reward_pool ) = each {
         # key ($archetype) is a simple regex to match against $class
        'warr|rang|shad|bard|rogu' => [
            # val 0 ($reward_pool->[0]) is reward list for bronze chunk
            [BRONZE_GLOOMINGDEEP_DAGGER..BRONZE_GLOOMINGDEEP_MACE],
            # val 1 ($reward_pool->[1]) is reward list for iron chunk
            [IRON_GLOOMINGDEEP_DAGGER..IRON_GLOOMINGDEEP_MACE],
        ],
        'cler|drui|monk|sham' => [
            [BRONZE_GLOOMINGDEEP_MACE],
            [IRON_GLOOMINGDEEP_MACE],
        ],
        'necr|wiza|magi|ench' => [
            [BRONZE_GLOOMINGDEEP_DAGGER],
            [IRON_GLOOMINGDEEP_DAGGER],
        ],
        'pala' => [
            [BRONZE_GLOOMINGDEEP_SWORD, BRONZE_GLOOMINGDEEP_MACE],
            [IRON_GLOOMINGDEEP_SWORD, IRON_GLOOMINGDEEP_MACE],
        ],
        'beas' => [
            [BRONZE_GLOOMINGDEEP_DAGGER, BRONZE_GLOOMINGDEEP_MACE],
            [IRON_GLOOMINGDEEP_DAGGER, IRON_GLOOMINGDEEP_MACE],
        ],
        'berz' => [
            [BRONZE_GLOOMINGDEEP_AXE],
            [IRON_GLOOMINGDEEP_AXE],
        ]
    # returns on first archetype match and successful item summon
    } ) { return 1 if $class =~ /$archetype/i && $summon_from->( $reward_pool->[$value] ) }
}

# grouping stuff this way helps with abstraction
my $forged_weapon_for = sub {
    # these variables control everything
    my ( $class, $material ) = @_;
    my $value = ( $material == CHUNK_OF_IRON );
    # single-line, dynamic quest::say example
    quest::say("Now let me see... Ah ha! Here ya go! ".(
      # ternary op controls which additional text is used
      $value
        # first is for chunk of iron turnin
        ? "A spiffy, new weapon to aid you in your adventures!"
        # second is for chunk of bronze turnin
        : "A much better weapon to help fend off those nasties!"
    ));
    # call dispatch table for weapon forging
    return $forge_weapon->($class, $value);
};

# everything in the event is clearly defined elsewhere in the script
# this makes it easier to have an overview of what the event does
sub EVENT_ITEM {
    # ! WARNING: make sure you have a path to short circuit ...
    # we define a list of trigger => responses pairs here
    while ( my ($condition_met, $action_taken) = each {
        # scenario group: improve existing weapon
        $accepted_broken->(DAGGER)      => $returned_repaired->(SHARPENED_DAGGER),
        $accepted_broken->(SHORT_SWORD) => $returned_repaired->(SHARPENED_SHORT_SWORD),
        $accepted_broken->(CLUB)        => $returned_repaired->(POLISHED_CLUB),
        $accepted_broken->(DULL_AXE)    => $returned_repaired->(SHARPENED_AXE),
        # scenario group: forge new weapon
        $accepted_metal->(CHUNK_OF_BRONZE) => $forged_weapon_for->($class, CHUNK_OF_BRONZE),
        $accepted_metal->(CHUNK_OF_IRON)   => $forged_weapon_for->($class, CHUNK_OF_IRON)
    # short circuit on first instance of condition_met AND return of 1 from action_taken
    } ) { last if $condition_met && $action_taken } # ! ... or loop forever!
    # return what we didn't use
    $return_unused_stuff;
}
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote