Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Nilrin

Pages: [1] 2
1
Code Vault / Read and Write on Parchment
« on: December 20, 2014, 09:04:23 AM »
Someone asked me how I did this, for some silly reason...

Anyway, this lets you write using stationary and a writing utensil.

Here's the verb write.c:

Code: [Select]
/*    /verbs/players/write.c
 *    from the Rebirth Library
 *    created by Nilrin
 */

#include <lib.h>
#include <position.h>
#include <rounds.h>

inherit LIB_VERB;

static void create() {
    verb::create();
    SetVerb("write");
    SetRules("on OBJ with OBJ");
    SetErrorMessage("Write on what with what?");
    SetHelp("Syntax: write on <item> with <item>\n\n"
            "Allows you to write something on the specified object using the specified writing tool."
            "\nSee also:");
}

int end_write(object ob1, string tmp){
    if(ob1 && tmp){
        ob1->SetFileSource(tmp);
    }
    send_messages("stop","$agent_name $agent_verb writing on $target_name.",this_player(),ob1,"subject_env",0,({ "visual" }));
    return 1;
}

mixed can_write_on_obj_with_obj() {
    if( this_player()->GetParalyzed() ) {
        return "You cannot do anything!";
    }
    return this_player()->CanManipulate();
}

mixed do_write_on_obj_with_obj(object ob1, object ob2){
        string file = ob1->GetFileSource();

        if(file){
            this_player()->eventEdit(file, (: end_write, ob1 :));
            send_messages("begin","$agent_name $agent_verb writing on $target_name.",this_player(),ob1,"subject_env",0,({ "visual" }));
        }
        else{
            string dir = "/doc/writings/";
            string tmp;

            if(!directory_exists(dir)){
                mkdir(dir);
            }

            tmp = dir + ob1->GetKeyName() + sizeof(get_dir(dir));

            this_player()->eventEdit(tmp, (: end_write, ob1, tmp :));
            send_messages("begin","$agent_name $agent_verb writing on $target_name.",this_player(),ob1,"subject_env",0,({ "visual" }));
        }
    return 1;
}

And here is parchment.c

Code: [Select]
#include <lib.h>
#include <vendor_types.h>
#include <damage_types.h>

inherit LIB_ITEM;
inherit LIB_PERSIST;
inherit LIB_READ;

public string file_source;

string GetFileSource(){
    return file_source;
}

string SetFileSource(string str){
    file_source = str;
    SetLong("This is a sheet of parchment. It has been made from the skin of an animal and stretch out extremely thin, to the point of being partially transparent.\nThe parchment has something written on it.");
    return str;
}

eventRead(){
    if(file_source && file_exists(file_source)){
        this_player()->eventPage(file_source);
        send_messages("read","$agent_name $agent_verb $target_name.",this_player(),this_object(),"subject_env",0,({ "visual","exclude_subject" }));
    }
    else{
        write("There is nothing written on it.");
    }
    return 1;
}

void create(){
    item::create();
    SetKeyName("parchment");
    SetId(({"parchment","sheet of parchment","sheet"}));
    SetShort("a sheet of parchment");
    SetLong("This is a sheet of parchment. It has been made from the skin of an animal and stretch out extremely thin, to the point of being partially transparent.");
    SetMass(1);
    SetVendorType(VT_TREASURE);
    SetRead((: eventRead :));
    AddSave( ({ "file_source" }) );
}

void init(){
    ::init();
}

int direct_write_on_obj_with_obj(object ob1, object ob2){
    return 1;
}

Whatever you want to use as a writing utensil, just have:

int indirect_write_on_obj_with_obj(object ob1, object ob2){
    return 1;
}

Also, you'll want to change the send_messages() functions to whatever your message handler/print to screen thingie is.

Pretty simple really.

2
General / NetTerm using netedit.c
« on: December 04, 2014, 02:44:57 PM »
Does anyone use netedit with the NetTerm telnet client to remotely edit files on their MUD? I'm curious if anyone has experience implementing this.

Back in the day, I used to work on a MUD named Ascension that used this to edit files. You just typed 'netedit <filename>' and it would send the file to NetTerm which would automatically launch your text editor. Then when you saved the file, and it would send the file back to the MUD. I'm not sure how they implemented the netedit.c file with their MUD though.

3
General / Parser Ignoring An Object?
« on: August 17, 2014, 09:58:18 AM »
I'm curious if anyone knows a method to get the command parser to completely ignore an object.

I've worked with verbs plenty before. Generally, you use your direct and indirect functions to tell the verb if you want to allow something to interact with that object.
As an example here, we have a player who is invisible, and another player that suspects the first of being so. Currently, the player could try something like this:

> look nilrin
There is no nilrin here.
> look asdf
You can't look asdf.

Ah hah! So even though we get some coded error for Nilrin "not being there" when know he in fact is, due to the nature of the error message given.

Now, one work-around could be to try and make both error messages read the same. The problem there, is when the parser, or the very least, the verb, matches an argument to an actual object, there isn't any way to query the original argument the player used.

Either way, I think the best method would be to tell the parser to ignore an object all together if that object->GetInvis() returns true. Is this even possible?

4
Dead Souls Support / runtime log
« on: October 02, 2013, 03:47:18 PM »
FluffOS v2.23-ds01
Dead Souls 3.7a7 (lots of modifications though)

I'm running this on ubuntu.

So I was going through my log files just for the heck of it, as well as the archives folder, and noticed a whole lot of runtime log files. Each runtime backlog is upwards of 100kb or more, which, in itself is concerning. I do not receive any runtime error messages through the admin character or through the driver output window.

I wouldn't even bring this up, except for the fact that I simply can not open the log file (/log/runtime) or any of the large runtime backlog files in the archive folder.

What's more, is I've noticed that the runtime log generates its large file when I execute a warmboot.

With that being said, I have indeed modified relative functions that a warmboot might affect, such as the eventDestruct() function on room files and such. I accept that whatever the errors are, are likely because of my own modifications to the library. However, my concern lies in the fact that I simply can not open the log files in question. A 'head' or 'tail' command throws an error, and I can not open them through ubuntu using gedit or other text editing software because the programs either hang or open junk text, depending on the program.

The error when attempting to open the file through the MUD is:

Attempted to read '\0' into string!
Object: /secure/sefun/sefun at line 154

..and then the usual trace errors.

I suspect that the driver is generating corrupt runtime log files, but I can't explain why, and have no experience with FluffOS to know what I'm talking about.

Would it be logical to assume that updating FluffOS might solve the problem? I'm not sure where FluffOS has gotten to right now. I understand there may be forked development, or that newer versions might not agree with current libraries.

At any rate, are there any thoughts to what might be going on?

5
Dead Souls Support / Line Of Sight
« on: July 08, 2013, 02:34:04 PM »
Does anyone around have any experience with calculating line of sight on a grid between two points? I've done some research on the matter, and it seems a lot of people talk about ray tracing, but I'm not sure if that could entirely be applied towards MUDs and the Dead Souls grid system. Really all I'm hoping to accomplish is figuring line of sight from point A to point B. Ultimately I'm looking for a reliable 'scan' function that users can use as a limited view distance in a generalised cardinal direction. IE, scanning north will yield objects in view inside a cone pattern up to, say, five rooms in that general direction..

I'm thinking of just finding the slope from A to B and then finding any rooms that intersect, within reason, to the line. If there is a non room, or a room deemed as blockable, the remainder of the line is obscured from vision. This method seems crude but probably is all that's needed for something like a MUD.

Any thoughts or experiences on this?

6
Bug Central / Combat Bug
« on: June 25, 2013, 12:04:49 AM »
This was a bug discovered in Dead Souls 3.7a7.

In /lib/combat.c line 581 (Probably different for stock distribution).

Code: [Select]
    if( position == POSITION_LYING || position == POSITION_SITTING &&
            RACES_D->GetLimblessCombatRace(this_object()->GetRace()) != 1){
        if(this_object()->CanFly()){
            this_object()->eventFly();
        }
        else if(objectp(target) && (((this_object()->GetSize(1)) -
                        target->GetSize(1)) < 2) ){
            this_object()->eventPrint("You can't fight "+
                    target_name+" unless you are up!");
            return 0;
        }
    }

To:

Code: [Select]
    if( (position == POSITION_LYING || position == POSITION_SITTING) &&
            RACES_D->GetLimblessCombatRace(this_object()->GetRace()) != 1){
        if(this_object()->CanFly()){
            this_object()->eventFly();
        }
        else if(objectp(target) && (((this_object()->GetSize(1)) -
                        target->GetSize(1)) < 2) ){
            this_object()->eventPrint("You can't fight "+
                    target_name+" unless you are up!");
            return 0;
        }
    }

Just needs a parentheses in the 'IF' statement.

7
Code Vault / /daemon/soul.c
« on: February 27, 2013, 11:40:57 PM »

Not sure why, but /daemon/soul.c doesn't seem to have a RemoveAdverb(), even though various bits of code, including the removeadverb command makes calls to it.

Add this:

Code: [Select]
int RemoveAdverb(string array adv){
    validate();
    Adverbs = Adverbs - ({ adv });
    eventSave();
    return 1;
}

8
Dead Souls Support / Persistence w/ MUD Crash?
« on: February 08, 2013, 11:29:05 PM »
Here's the scoop. Currently I have a 'sort of persistence' scenario in which the majority of rooms in my MUD persist when destructed. Both tangible items and NPCs persist within these rooms.

Let's say, for instance, that there was -NPC rat- and he is lucky enough to be loaded into the MUD some place in the deep dark forest. Now, he was loaded there because the silly -player- came walking in and loaded a room that happened to have a persistence file, and within that persistence file was the saved -NPC rat-. When the -player- saw the -NPC rat-, he decided that it wasn't worth the experience, and wandered on his marry way. Ah, but -NPC rat- had his own plan, and went exploring the vastness that is the deep dark forest. Since the -NPC rat- and the -player- were both gone, there was nothing left for the room to persist, so it removed its persistence file.

However, before long, something strange happened. Who knows what caused it. Perhaps it was meant to be, or perhaps it was the great nothing, or an act of god, or perhaps the great -creator- of everything flipped his shoe off and bumped the power button on the world we call computer. The point is, everything ceased to be. The -player-, gone. The -NPC rat-, gone. The deep dark forest, and everything else, gone.

Fortunately, what was once nothing can become something again, thanks to the magic the -creator- refers to as reboot. The -player- logs back in to the MUD, the rooms will load into memory when they are ready, but what of the -NPC rat-?

The -NPC rat- lost his persistence file, was loaded in memory, but had not the chance to create himself a new persistence file in whatever room he found himself in while exploring. The -NPC rat- is effectively gone forever. We can only hope that one day the merciless -creator- will notice the generic yet brave -NPC rat- is missing from the world, and will be brought back into existence again with the power of -clone-.

So therein lies the problem.

I've been pondering this problem for a few days now and due to a lack of imagination (see story above if you don't believe me), I've only come up with a few solutions. Of them, only one really seems reliable. Still, I thought I would share this with others full well knowing I'm not the only one with this issue and has probably been solved before.

Solution 1: Have regular MUD reboots, which, upon ending, does a backup of the entire MUD.

Solution 2: Have a daemon persist all NPCs, even NPC rats, which are currently loaded in memory. Again, this would have to be at regular intervals.

Solution 3:  When specific (generic) NPCs are cloned, they tell a daemon that they just loaded into memory, “Persist me, just in case.” When eventDestruct is called within the same NPCs, they again inform the daemon, “Alright, I'm safely persisted in my own room now, you can remove my separate persistence file.” When the MUD boots up, it loads the daemon and within its create() function, it checks to see if there are any persistence files it created. If so, go ahead and load them in to memery.

Any thoughts?

9
Dead Souls Support / Verb Problem
« on: January 20, 2010, 05:52:26 PM »

I've worked with verbs plenty before, but it seems I lack the mental capacity to grasp this simple verb setup...

Code: [Select]
#include <lib.h>
#include <daemons.h>

mixed can_craft_obj(string verb);
mixed do_craft_obj(object item);

inherit LIB_VERB;

static void create() {
    verb::create();
    SetVerb("craft");
    SetRules("OBJ");
    SetErrorMessage("What would you like to craft?");
    SetHelp("Syntax: <craft OBJECT>\n"
            "        <craft ITEM from OBJECT>\n"
            "Allows you to craft an object.");
}

mixed can_craft_obj(string verb) {
    if( this_player()->GetParalized() ) {
        return "You cannot do anything.";
    }
    return this_player()->CanManipulate();
}

mixed do_craft_obj(object item) {
    write(item);
    return 1;
}


I can clone any random item in game, and attempt to use the verb on it, which yields:

You can't craft that.

10
Bug Central / Vendors and Bargaining
« on: November 24, 2009, 03:51:32 AM »

Unless I'm misunderstanding, there are essentially two functions you can currently set on an item, both of which determine an items value, though both work in different ways. SetValue() simply allows you to set an integer value to the item. SetBaseValue() allows you to actually set an array of currency and integer values.

The problem lies in vendor.c where, curious enough, holds code that uses both SetValue() and SetBaseValue(), but in different cases.

Further more, there are the beginnings of a Bargaining skill referenced in vendor.c, though it isn't used in its entirety. The problem with this, for example, is you could 'ask oana to price die' she would report that it would cost 15 silver, but then when you 'ask oana to sell die' she would sell it to you for 9 silver. This may have been deliberate, but I couldn't say for sure.

What's most problematic however is the partial makings of bargaining which allows for not cool things such as the possibility of selling an item to a store for more than the purchase price, hence unlimited moneys.  Also, if I remember right, some of the skill functionality worked backwards. IE: The higher your bargaining, the less money you could sell an item for.

I've poked around vendor.c for what seems like most of the day, and I think I have it working. I've tested it for a bit, but can't guarantee its perfect.  I've left in the commented out code, and also added comments on changes made within the code.

Here it is:

Code: [Select]
#include <lib.h>
#include <armor_types.h>
#include <vendor_types.h>
#include <daemons.h>
#include "/lib/include/vendor.h"

inherit LIB_SENTIENT;
inherit LIB_MANYCOINS;

private static int MaxItems, VendorType, bargain;
private static string StorageRoom, LocalCurrency;
private static mapping Costs, Values;

static void create(){
    sentient::create();
    Costs = ([]);
    Values = ([]);
    LocalCurrency = "gold";
    VendorType = VT_TREASURE;
    MaxItems = 40;
}

mixed direct_buy_str_from_liv(string str){
    return CanSell(this_player(), str);
}

int indirect_sell_obj_to_liv(){ return 1; }

int indirect_sell_obs_to_liv(){ return 1; }

int CanCarry(int amount){ return 1; }

    mixed CanBuy(object who, object *obs){
        if( !load_object(StorageRoom) )
            return "There is a bug with the vendor's storage, please report it.";
        return 1;
    }

    mixed CanSell(object who, string what){
        if( !load_object(StorageRoom) )
            return "There is a bug with the vendor's storage, please report it.";
        return 1;
    }

mixed eventBuy(object who, object *obs){
    object *tmp;
    object sroom, ob;
    int cost;

    sroom = load_object(StorageRoom);
    if( sizeof(all_inventory(sroom)) > GetMaxItems() ){
        eventForce("say I am having trouble getting rid of the things I "
                "have to sell right now.");
        return 1;
    }
    if( !sizeof(obs) ){
        eventForce("say you have nothing to sell!");
        return 1;
    }
    tmp = ({});
    foreach(ob in obs){
        int value;

        if( !(ob->GetShort()) ) continue;
        if( !(ob->GetVendorType() & GetVendorType()) ){
            eventForce("say I do not buy things like " +
                    ob->GetShort());
            continue;
        }
        cost = to_int(ob->GetBaseCost(GetLocalCurrency()));
        value = to_int(ceil(GetValue(ob, who)));
//      This shouldn't be in here. Items don't have their own GetValue anymore.
//      if(ob->GetValue()) bargain = 1;
//      else bargain = 0;
//      if(!bargain) value = cost;

        if( !cost || cost < 1 || !value || value < 1){
            eventForce("say " + ob->GetShort() + " is worthless!");
            continue;
        }
        if( !(ob->CanSell(who)) ){
            eventForce("say You cannot sell " + ob->GetShort() +".");
            continue;
        }
        if( sizeof(filter(all_inventory(sroom),
                        (: $1->GetShort() == $(ob)->GetShort() :)))
                > 3 ){
            if( this_player()->AddCurrency(GetLocalCurrency(),value) == -1 ){
                eventForce("say you cannot carry "+value+" "+
                        GetLocalCurrency()+"!");
                ob->eventMove(environment());
                continue;
            }
            eventForce("say " + ob->GetShort() + "! Great!");
            tell_player(this_player(),GetShort()+" gives you "+value+
                    " "+GetLocalCurrency()+".");
            if(bargain) who->AddSkillPoints("bargaining", value*5);
            message("my_action", "You sell " + ob->GetShort() + ".", who);
            message("other_action", capitalize(who->GetKeyName()) + " sells " +
                    ob->GetShort() + ".", environment(),
                    ({ who, this_object() }));
            ob->eventDestruct();
            return 1;
        }
        eventForce("say " + ob->GetShort() + "! Excellent!");
        tell_player(this_player(),GetShort()+" pays you "+value+
                " "+GetLocalCurrency()+".");

        if( !(ob->eventMove(sroom)) ){
            eventForce("say I cannot seem to carry that");
            return 1;
        }
        if( this_player()->AddCurrency(GetLocalCurrency(), value) == -1 ){
            eventForce("say you cannot carry "+value+" "+
                    GetLocalCurrency()+"!");
            ob->eventMove(environment());
            continue;
        }
        if(bargain) who->AddSkillPoints("bargaining", value*5);
        tmp += ({ ob });
        message("my_action", "You sell " + ob->GetShort() + ".", who);
        message("other_action", capitalize(who->GetKeyName()) + " sells " +
                ob->GetShort() + ".", environment(),
                ({ who, this_object() }));
    }
    if( !sizeof(tmp) )
        eventForce("say I am sorry, " + capitalize(who->GetKeyName()) + ", "
                "that we could not come to a better agreement.");
    else map(tmp, function(object ob){
            if( ob->GetDestroyOnSell() )
            ob->eventDestruct();
            });
    return 1;
}

int cmdShow(object who, string args){
    object ob, sroom;
    int x;

    if( !args || args == "" ){
        eventForce("say show you what?");
        return 1;
    }
    if( !(sroom = load_object(StorageRoom)) ){
        eventForce("say I am having troubles right now");
        return 1;
    }
    if( x = to_int(args) ){
        object *obs;
        object *obs2;
        string *list2;
        int maxi;

        x--;
        obs = all_inventory(sroom);
        maxi = sizeof(obs);
        list2 = ({(obs[0]->GetKeyName())});
        for(int counter = 1;counter < maxi;++counter){
            if(member_array((obs[counter]->GetKeyName()),list2) < 0){
                list2 += ({ ( obs[counter]->GetKeyName()) });
            }
        }
        obs2 = ({ present(list2[0],sroom) });
        maxi = sizeof(list2);
        for(int counter = 1;counter < maxi;++counter){
            obs2 += ({ present(list2[counter],sroom) });
        }
        if((x >= 0) && (x < sizeof(obs2))){
            ob = obs2[x];
        }
    }
    else ob = present(args = lower_case(args), sroom);
    if( !ob ){
        eventForce("say I have no such thing to show you");
        return 1;
    }
    message("other_action", capitalize(GetKeyName())+" shows you "+
            ob->GetShort()+".", who);
    message("system", ob->GetLong(), who);
    return 1;
}

int cmdBrowse(object who, string args){
    object *obs;
    object *obs2;
    string *list;
    object sroom;
    int i, ii, maxi, number;

    if( !args || args == "" ) args = "all";
    if( !(sroom = load_object(StorageRoom)) ){
        eventForce("say I am having terrific difficulties today");
        return 1;
    }
    if(!(maxi = sizeof(obs = filter(all_inventory(sroom), (: !userp($1) :))))){
        eventForce("say I have nothing to sell right now.");
        return 1;
    }
    list = ({ " #  Description", "" });
    obs2 = ({});
    foreach(object tempob in obs){
        string *base_names = ({});
        foreach( object tempob2 in obs2 ){
            base_names += ({ base_name(tempob2) });
        }
        if(member_array(base_name(tempob), base_names) == -1){
            obs2 += ({ tempob });
        }
    }
    maxi = sizeof(obs2);
    i = this_player()->GetScreen()[0];
    if(number = to_int(args)){
        if((number > 0) && (number <= maxi)){
            int tmp;
            while( i-- ) list[1] += "_";
            if(intp(number) &&
                    (tmp = to_int(ceil(GetCost(obs2[(number - 1)],who))))){
                list += ({ sprintf("%:-3d %:-35s", number,
                            obs2[(number - 1)]->GetShort(), tmp) });
            }
            this_player()->eventPage(list);
            return 1;
        }
    }

    while( i-- ) list[1] += "_";
    for(ii=0; ii < maxi; ii++){
        int ok;
        int gat;

        ok = 0;
        gat = (obs2[ii]->GetArmorType());
        switch(args){
            case "all": ok = 1; break;
            case "weapon": case "weapons":
                break;
            case "armor": case "armors":
                if(obs2[ii]->GetVendorType() == 8){
                    ok = 1;
                }
            break;
            case "bag": case "bags":
                ok = obs2[ii]->GetProperty("bag");
            break;
            case "ring": case "rings":
                ok = gat & A_RING;
            break;
            case "glove": case "gloves":
                ok = gat & (A_GLOVE | A_LONG_GLOVE);
            break;
            case "boot": case "boots":
                ok = gat & (A_BOOT | A_LONG_BOOT);
            break;
            case "sock": case "socks":
                ok = gat & (A_SOCK | A_LONG_SOCK);
            break;
            case "helm": case "helmet":
                ok = gat & A_HELMET;
            break;
            case "visor": case "visors":
                ok = gat & A_VISOR;
            break;
            case "pants":
                ok = gat & A_PANTS;
            break;
            case "shirt": case "shirts":
                ok = gat & A_SHIRT;
            break;
            case "cloak": case "cloaks":
                ok = gat & A_CLOAK;
            break;
            case "belt": case "belts":
                ok = gat & A_BELT;
            break;
            case "vest": case "vests":
                ok = gat & A_VEST;
            break;
            case "shield": case "shields":
                ok = gat & A_SHIELD;
            break;
            case "body armor": case "body armors":
                ok = gat & A_BODY_ARMOR;
            break;
            case "blunt": case "knife": case "blade": case "projectile":
                case "blunts": case "knives": case "blades": case "projectiles":
                ok = (obs2[ii]->GetWeaponType() == args) ||
                (pluralize((obs2[ii]->GetWeaponType() || "")) == args);
            break;
            default:
            ok = obs2[ii]->id(args);
            break;
        }
        if( !ok ) continue;
        ok = GetCost(obs2[ii], who);
        if(!ii) ii = 0;
        list += ({ sprintf("%:-3d %:-35s", (ii+1), obs2[ii]->GetShort(), to_int(ok)) });
    }
    if( !sizeof(list) ){
        eventForce("frown");
        eventForce("say I have nothing like that to sell.");
        return 1;
    }
    this_player()->eventPage(list);
    return 1;
}

int cmdAppraise(object who, string args){
    object ob;
    int x,cost;

    if( !args || args == "" ){
        eventForce("say appraise what?");
        return 1;
    }
    if( !(ob = present(args = lower_case(args), who)) ){
        eventForce("say You have no such thing!");
        return 1;
    }
    if( !(ob->GetVendorType() & GetVendorType()) ){
        eventForce("say I have no use for " + ob->GetShort());
        return 1;
    }
    if( LocalCurrency != query_base_currency() ){
//      Code is shouldn't overide the bargaining skill here.
//      cost=to_int(ob->GetBaseCost() / ECONOMY_D->__Query(LocalCurrency,"rate"));
        cost=to_int(GetValue(ob,who) / ECONOMY_D->__Query(LocalCurrency,"rate"));
    }
//  Another place where code completely overides bargaining for some reason.
//  else cost = ob->GetBaseCost();
    cost = GetValue(ob,who);
    if(!cost || cost < 1){
        eventForce("say " + capitalize(who->GetKeyName()) + ", I will not buy "
                "that worthless thing from you.");
        return 1;
    }
    else x=cost;
    eventForce("say " + capitalize(who->GetKeyName()) + ", I will offer "
            "you " + x + " " + GetLocalCurrency() + " for " +
            ob->GetShort());
    return 1;
}

int cmdPrice(object who, string args){
    object *obs;
    string *list2;
    object *obs2;
    object ob, sroom;
    int x;
    int maxi;

    if( !args || args == "" ){
        eventForce("say price what?");
        return 1;
    }
    if( !(sroom = load_object(StorageRoom)) ){
        eventForce("say today is not really a good day for me");
        return 0;
    }
    obs = all_inventory(sroom);
    obs2 = ({});
    foreach(object tempob in obs){
        string *base_names = ({});
        foreach( object tempob2 in obs2 ) base_names += ({ base_name(tempob2) });
        if(!sizeof(obs2)){
            obs2 = ({tempob});
        }
        else if(member_array(base_name(tempob), base_names) == -1) obs2 += ({ tempob });
    }
    maxi = sizeof(obs2);
    if( x = to_int(args) ){
        list2 = ({(obs2[0]->GetKeyName())});
        for(int counter = 1;counter < maxi;++counter){
            if(member_array((obs2[counter]->GetKeyName()),list2) < 0){
                list2 += ({ ( obs2[counter]->GetKeyName()) });
            }
        }
        maxi = sizeof(list2);
        ob = obs2[x-1];
    }
    else ob = present(args = lower_case(args), sroom);
    if(!ob) foreach(object tempob in obs2)
        if(answers_to(args,tempob)) ob = tempob;
    if( !ob ){
        eventForce("say I have no such thing!");
        return 1;
    }
    if( !(x = to_int(ceil(GetCost(ob, this_player())))) ){
        eventForce("say that thing has no value!");
        return 1;
    }
    eventForce("say " + capitalize(who->GetKeyName()) + ", I will take " +
            x + " " + GetLocalCurrency() + " for " +
            ob->GetShort());
    return 1;
}

mixed eventAsk(object who, string str){
    object *obs;
    object ob;
    string cmd, args, tmp;
    string lang, prof, orig = str;

    lang = who->GetDefaultLanguage();
    prof = who->GetLanguageLevel(lang);
    str = translate(str, prof);
    prof = this_object()->GetLanguageLevel(lang);
    str = translate(str, prof);

    if( !str || str == "" ){
        eventForce("say what do you want from me?");
        return 1;
    }
    if( sscanf(str, "%s %s", cmd, args) != 2 ){
        cmd = str;
        args = 0;
    }
    switch(cmd){
        case "appraise":
            return cmdAppraise(who, args);

        case "browse":
            return cmdBrowse(who, args);

        case "buy":
            if( str == "all" )
                obs = filter(all_inventory(who), (: $1->CanSell() :));
            else {
                if( !(ob = present(args, who)) ){
                    eventForce("say Get out of here you cheat!");
                    eventForce("bump " + this_player()->GetKeyName());
                    return 1;
                }
                obs = ({ ob });
            }
        return eventBuy(who, obs);

        case "price":
            return cmdPrice(who, args);

        case "sell":
            if( (tmp = CanSell(who, args)) == 1 ) return eventSell(who, args);
            else if( stringp(tmp) ) eventForce("say " + tmp);
            else {
                return 0;
            }

        case "show":
            return cmdShow(who, args);

        default:
        //Thx Raudhrskal
        if(!sentient::eventAsk(who,orig))
            eventForce("say I am not quite sure what you want from me");
    }
}

mixed eventSell(object who, mixed what){
    object ob, sroom;
    object *obs;
    string *list2;
    object *obs2;
    int cost,number,maxi;

    sroom = load_object(StorageRoom);
    obs = filter(all_inventory(sroom), (: !userp($1) :));
    obs2 = ({});
    foreach(object tempob in obs){
        string *base_names = ({});
        foreach( object tempob2 in obs2 ) base_names += ({ base_name(tempob2) });
        if(!sizeof(obs2)){
            obs2 = ({tempob});
        }
        else if(member_array(base_name(tempob), base_names) == -1) obs2 += ({ tempob });
    }
    maxi = sizeof(obs2);
    if(number = to_int(what)){
        number = number - 1;
        list2 = ({(obs2[0]->GetKeyName())});
        for(int counter = 1;counter < maxi;++counter){
            if(member_array((obs2[counter]->GetKeyName()),list2) < 0){
                list2 += ({ ( obs2[counter]->GetKeyName()) });
            }
        }
        maxi = sizeof(list2);
        if((number >= 0) && (number < sizeof(obs2))){
            ob = obs2[number];
        } else {
            eventForce("say I have nothing like that to sell to you.");
            return 1;
        }     
    } else {
        if( !(ob = present(what, sroom)))
            foreach(object tempob in obs2)
                if(answers_to(what,tempob)) ob = tempob;
        if(!ob || userp(ob)){
            eventForce("say I have nothing like that to sell you.");
            return 1;
        }
    }
    cost=to_int(ob->GetBaseCost(GetLocalCurrency()));
    cost = GetCost(ob,who);
    if(!cost || cost < 0){
        cost = 0;
        cost = to_int(ob->GetValue());
    }
    if(who->GetCurrency(GetLocalCurrency()) < cost){
        eventForce("say You don't have enough "+GetLocalCurrency()+
                " to buy that.");
        return 1;
    }
    if( !(ob->eventMove(this_object())) ){
        message("error", "An error occurred moving the object, use bug -r.",
                who);
        return 1;
    }
    eventForce("say here is " + ob->GetShort() + " for " + to_int(cost) +
            " " + GetLocalCurrency() + "!");
    eventForce("give " + ob->GetUniqueId() + " to " + who->GetKeyName());
    if( environment(ob) == this_object() ){
        eventForce("say you cannot carry that!");
        eventForce("drop " + ob->GetKeyName());
    }
    if(bargain) who->AddSkillPoints("bargaining", random(to_int(floor(cost))));
    who->AddCurrency(GetLocalCurrency(), -cost);
    return 1;
}

int GetCost(object ob, object who){
    int x, mod,cost;

    cost=ob->GetBaseCost(LocalCurrency);
//  Why is this even in here?
//  if(cost && cost > 0) return cost;

    if( Costs[who] && Costs[who][ob] ) return Costs[who][ob];   
    x = ob->GetValue(GetLocalCurrency());
    mod = who->GetSkillLevel("bargaining") - GetSkillLevel("bargaining");
    if( mod < -90 ) mod = -90;
    if( mod > 90 ) mod = 90;
    x = (x * (100 - mod))/100;

//  Added the following line so that you can't pay less than value price for an item.
    x+=ob->GetValue(GetLocalCurrency());
    who->AddSkillPoints("bargaining", x);
    if( !Costs[who] ) Costs[who] = ([ ob : x ]);
    else Costs[who][ob] = x;
    return x;
}

int GetValue(object ob, object who){
    int x, mod;
    if( Values[who] && Values[who][ob] ) return Values[who][ob];
    x = ob->GetValue(GetLocalCurrency());
//  Switched these two around to reflect who benifits from which side of the deal.
    mod = GetSkillLevel("bargaining") - who->GetSkillLevel("bargaining");
    if( mod < -90 ) mod = -90;
    if( mod > 90 ) mod = 90;
//  Changed to devide by 200. This way, the best deal is 90 percent of regular price.
    x = (x * (100 - mod)) / 200;
    who->AddSkillPoints("bargaining", x);
    if( !Values[who] ) Values[who] = ([ ob : x ]);
    else Values[who][ob] = x;
    return x;
}

string SetLocalCurrency(string str){ return (LocalCurrency = str); }

string GetLocalCurrency(){ return LocalCurrency; }

string SetStorageRoom(string room){ return (StorageRoom = room); }

string GetStorageRoom(){ return StorageRoom; }

int SetMaxItems(int x){ return (MaxItems = x); }

int GetMaxItems(){ return MaxItems; }

int SetVendorType(int x){ return (VendorType = x); }

int GetVendorType(){ return VendorType; }






11
Code Vault / 'Smart' Mouse (NPC using astar)
« on: November 05, 2009, 01:03:50 AM »

A little while back I wanted to see if I could get an NPC to use the A* algorithm.  This is what I have thus far, quick and dirty, but it works.

Call SetGoal( x, y ) in the NPC object to give it a coordinate goal. It works off of the Dead Souls II grid system and the astar efun Tricky made, meaning you already need astar code for this to work. The call to it is find_path().

This is the quick and dirty NPC.

Code: [Select]

#include <lib.h>
#include <daemons.h>

inherit LIB_SENTIENT;

int timer, positionx, positiony, goalx, goaly;

mixed *path_ret, *path;
string *command_list = ({});
mapping coordinates;

int xoffset = 0;
int yoffset = 8;
int step = 0;
int que_position = 0;

string tmp;

SetGoal(int x, int y){
    goalx = x + xoffset;
    goaly = (y*-1) + yoffset;
}

NextStep(){

    if(sizeof(command_list) > 0){

        eventForce(command_list[que_position]);
        que_position++;

        if(sizeof(command_list) == que_position ){
            que_position=0;
            command_list = ({});
        }
        return;
    }

    if( step >= sizeof(path) ){ return; }

    if(path[step] == "n"){ eventForce("go north"); step ++; return; }
    if(path[step] == "e"){ eventForce("go east"); step ++; return; }
    if(path[step] == "s"){ eventForce("go south"); step ++; return; }
    if(path[step] == "w"){ eventForce("go west"); step ++; return; }

    if(path[step] == "ne"){
        if(environment(this_object())->GetExit("northeast")){ eventForce("go northeast"); step++; return; }
        if(tmp = environment(this_object())->GetExit("east")){
            if(tmp->GetExit("north")){
                eventForce("go east");
                command_list += ({"go north"});
                step ++;
                return;
            }
        }
        if(tmp = environment(this_object())->GetExit("north")){
            if(tmp->GetExit("east")){
                eventForce("go north");
                command_list += ({"go east"});
                step ++;
                return;
            }
        }
    }
    if(path[step] == "se"){
        if(environment(this_object())->GetExit("southeast")){ eventForce("go southeast"); step++; return; }
        if(tmp = environment(this_object())->GetExit("east")){
            if(tmp->GetExit("south")){
                eventForce("go east");
                command_list += ({"go south"});
                step ++;
                return;
            }
        }
        if(tmp = environment(this_object())->GetExit("south")){
            if(tmp->GetExit("east")){
                eventForce("go south");
                command_list += ({"go east"});
                step ++;
                return;
            }
        }
    }
    if(path[step] == "sw"){
        if(environment(this_object())->GetExit("southwest")){ eventForce("go southwest"); step++; return; }
        if(tmp = environment(this_object())->GetExit("west")){
            if(tmp->GetExit("south")){
                eventForce("go west");
                command_list += ({"go south"});
                step ++;
                return;
            }
        }
        if(tmp = environment(this_object())->GetExit("south")){
            if(tmp->GetExit("west")){
                eventForce("go south");
                command_list += ({"go west"});
                step ++;
                return;
            }
        }
    }
    if(path[step] == "nw"){
        if(environment(this_object())->GetExit("northwest")){ eventForce("go northwest"); step++; return; }
        if(tmp = environment(this_object())->GetExit("west")){
            if(tmp->GetExit("north")){
                eventForce("go west");
                command_list += ({"go north"});
                step ++;
                return;
            }
        }
        if(tmp = environment(this_object())->GetExit("north")){
            if(tmp->GetExit("west")){
                eventForce("go north");
                command_list += ({"go west"});
                step ++;
                return;
            }
        }
    }
}

CheckPath(){

    coordinates = ROOMS_D->GetCoordinateMap(environment(this_object()));
    positionx = coordinates["x"] + xoffset;
    positiony = (coordinates["y"]*-1) + yoffset;
    path_ret = find_path(read_file("/domains/Kahara/npc/path_map"),positionx,positiony,goalx,goaly, ([ '#': 1 ]));
    //eventForce("emote " + path_ret[0]);
    path = explode(path_ret[0]," ");
    step = 0;
   
    return;
}

heart_beat(){

    timer++;

    if(timer >= 5){
        timer = 0;
        CheckPath();
    }
    NextStep();
}


static void create() {

    sentient::create();

    SetKeyName("mouse");

    SetId( ({"mouse", "rodent"}) );

    SetAdjectives(({"small", "dark-grey"}));

    SetShort("a small dark-grey mouse");

    SetLong("This is a small dark-grey mouse with small grey feet and a grey tail. His body is only a few inches long with a pointed snout, long whiskers and round ears.");

    SetLevel(1);

    SetMelee(1);

    SetRace("rodent");

    SetGender("male");

    SetWanderSpeed(20);

    SetAction(5, ({

        "A small dark-grey mouse squeeks quietly.",

        "A small dark-grey mouse sucrries about.",

        }));
    set_heart_beat(2);
    coordinates = ([]);

}

void init(){

    ::init();

}


This is the path map A* needs.

Code: [Select]
#                       \n
#                       \n
 #               ###    \n
 #             ##   ##  \n
 ##           #       # \n
 #           #        # \n
#           #         # \n
#           #          #\n
##         #           #\n
  ####   ###          # \n
      ###   ####     #  \n
                #    #  \n
                #  ##   \n
                 ##     

There are obvious drawbacks.

1. You need a map file for this to work.

2. Instead of the NPC calling A* every step of the way, it stores the entire path, then rechecks his path every so often to make sure he's still on the right track. I did this so there wasn't hundreds of NPCs calling the algorithm every heartbeat.

My main thoughts for this was to use it for NPC daily events, sorta how Oblivion works.

Also, there had to be a bit of a trick to getting it to understand going around corners, since on the map, there's no way to distinguish between an L corner and a diagonal path.

12
Code Vault / Genesis Trees
« on: August 06, 2009, 12:20:59 PM »

Basically a tree that grows and spawns new seeds, following certain 'environmental' rules.

Written in DS2.9a13 but should work with most DS libs.

generic_seed.c

Code: [Select]
// Generic Genesis Seed
// By Nilrin from Rebirth
// Created on 8/5/2009

#include <lib.h>
#include <vendor_types.h>
inherit LIB_ITEM;

int age = 0;
int nutrients = 1;
int starvation = 0;  //Older trees can starve longer without dying.
int is_dead = 0;
int time_to_seed = 0;

//This is only called when a tree is starving, to try and help keep
//it alive. Be sure that the nutrients that are subtracted here is
//the same number as are added in the branch file.

//Branches allow for three things. They can be picked up and moved
//unlike trees, which allows for people to 'fertalize' different
//areas. They also add to realism, but most importantly, if we didn't
//have these or another similar feature, seedlings would too easily
//starve out the big ole trees.

eventBranch() {

    if (!random(3)) {
        nutrients -= 200;
        clone_object("/domains/new/obj/generic_branch.c")->eventMove(environment());
        return;
    }

}

//Chance for a tree to drop one or more seeds. Depending on how many
//exits there are, there's more of a chance to drop a seed in the same
//room than in a adjacent one.

eventSeed() {

    time_to_seed += 1;

    if (time_to_seed > 20 && starvation <= 0) {
        time_to_seed = 0;
        if (!random(4)) {
            clone_object("/domains/new/obj/generic_seed.c")->eventMove(environment());
        }
        foreach(string exit in (string *)environment()->GetExits()){
            if (!random(20)) {
                clone_object("/domains/new/obj/generic_seed.c")->eventMove(environment()->GetExit(exit));
                nutrients -= 1;
            }
        }
    }
}

//General aging of the tree and consumption of nutrients. This should
//be changed ultimately so that short and long descriptions are not set
//at every age mark for optimization purposes.

eventAge() {

    if (age >= 0 && age <= 9) {
        if (environment()->GetNutrients() >= 1) {
            environment()->SubtractNutrients(0);
            SetShort("a generic seed");
            SetLong("This is a generic seed.");
            age += 1;
            return;
        }
    }

    if (age >= 10 && age <= 49) {
        if (environment()->GetNutrients() >= 1) {
            environment()->SubtractNutrients(1);
            nutrients += 1;
            SetShort("a generic seedling");
            SetLong("This is a generic seedling.");
            age += 1;
            return;
        }

        else {
            SetShort("a generic dead seedling");
            SetLong("This is a generic dead seedling");
            is_dead = 1;
        }
    }

    if (age >= 50 && age <= 99) {
        if (environment()->GetNutrients() >= 5) {
            environment()->SubtractNutrients(5);
            nutrients += 5;
            SetShort("a generic sapling");
            SetLong("This is a generic sapling.");
            age += 1;
            if (starvation > 0) {
                starvation -= 1;
            }
            return;
        }

        else {
            starvation += 1;
            if (starvation > 5) {
                SetShort("a generic dead sapling");
                SetLong("This is a generic dead sapling.");
                is_dead = 1;
            }
        }
    }

    if (age >= 100 && age <= 999) {
        if (environment()->GetNutrients() >= 10) {
            environment()->SubtractNutrients(10);
            nutrients += 10;
            SetShort("a generic tree");
            SetLong("This is a generic tree.");
            eventSeed();
            age += 1;
            if (starvation > 0) {
                starvation -= 1;
                eventBranch();
            }
            return;
        }

        else {
            starvation += 1;
            if (starvation > 10) {
                SetShort("a generic fallen tree");
                SetLong("This is a generic fallen tree.");
                is_dead = 1;
            }
        }
    }

    if (age >= 1000 && age <= 9999) {
        if (environment()->GetNutrients() >= 5) {
            environment()->SubtractNutrients(5);
            nutrients += 5;
            SetShort("a generic old tree");
            SetLong("This is a generic old tree.");
            eventSeed();
            age += 1;
            if (starvation > 0) {
                starvation -= 1;
                eventBranch();
            }
            return;
        }

        else {
            starvation += 1;
            if (starvation > 50) {
                SetShort("a generic old fallen tree");
                SetLong("This is a generic old fallen tree.");
                is_dead = 1;
            }
        }
    }

    if (age >= 10000) {
        if (environment()->GetNutrients() >= 2) {
            environment()->SubtractNutrients(2);
            nutrients += 2;
            SetShort("a generic ancient tree");
            SetLong("This is a generic ancient tree.");
            eventSeed();
            age += 1;
            if (starvation > 0) {
                starvation -= 1;
                eventBranch();
            }
            return;
        }

        else {
            starvation += 1;
            if (starvation > 100) {
                SetShort("a generic ancient fallen tree");
                SetLong("This is a generic ancient fallen tree.");
                is_dead = 1;
            }
        }
    }
}

//Decay of already dead trees.

eventDecay() {

    if (nutrients >= 50) {
        environment()->AddNutrients(20);
        nutrients -= 20;
        return;
    }
    if (nutrients < 50) {
        environment()->AddNutrients(nutrients);
        nutrients = 0;
        destruct(this_object());
    }

    else {
        destruct(this_object());
    }
}

void create(){

    int a;   

    ::create();
    SetKeyName("seed");
    SetId( ({"tree", "seed", "sapling", "seedling"}) );
    SetAdjectives( ({"generic","sample","template"}) );
    SetShort("a generic seed");
    SetLong("This is a generic seed.");
    SetMass(20);
    SetBaseCost("silver",10);
    SetVendorType(VT_TREASURE);

//Change to whatever. A heart beat of 1 is good for testing, but in
//very large areas gets out of hand and will strangle your cpu.

    a = random(30) + 60;
    set_heart_beat(a);
}

mixed CanGet(object ob) {

    if (age >= 10) {
        return 0;
    }

    else {
        return 1;
    }
}

void init(){
    ::init();
}

heart_beat(){

    if (is_dead == 1) {
        eventDecay();
    }

    else {
        eventAge();
    }
}

generic_branch.c

Code: [Select]
//Branch to be used with Genesis seed.
// By Nilrin from Rebirth
// Created on 8/5/2009

#include <lib.h>
#include <vendor_types.h>
inherit LIB_ITEM;

int nutrients = 200;

eventDecay() {

    if (nutrients >= 10) {
        environment()->AddNutrients(10);
        nutrients -= 10;
        return;
    }

    if (nutrients < 10) {
        environment()->AddNutrients(nutrients);
        nutrients = 0;
        destruct(this_object());
    }
}
void create(){
    ::create();
    SetKeyName("branch");
    SetId( ({"thing","item","thang","dingus"}) );
    SetAdjectives( ({"generic","sample","template"}) );
    SetShort("a generic branch");
    SetLong("This is a generic branch.");
    SetMass(20);
    SetBaseCost("silver",10);
    SetVendorType(VT_TREASURE);
    set_heart_beat(1);
}
void init(){
    ::init();
}

heart_beat() {
    eventDecay();
}

These query functions plus the nutrients variable need to be in any rooms you want trees to grow in.

Code: [Select]
int nutrients = 10000;

SetNutrients(int a) {
    nutrients = a;
}

GetNutrients() {
    return nutrients;
}

SubtractNutrients(int a) {
    nutrients = nutrients - a;
}

AddNutrients(int a) {
    nutrients = nutrients + a;
}

Not sure this has any practical purpose. Having 20,000+ objects with heartbeats in my virtual room wilderness probably isn't the best way to go about things. Surely this could be used small scale in things like flower beds, vegetable gardens, etc...

By the way, this is, by no means, optimized code. Use at your own risk. ;)

13
Dead Souls Support / More Buggy Shops and Unique IDs
« on: January 03, 2009, 03:41:49 AM »
I'm experiencing some peculiar things with shops I just can't seem to get sorted.

I actually found this first quirk when I was trying to figure out the workings of the bargaining skill, SetValue and SetBaseCost.
I first noticed it when I was experimenting with items from a domain I created a while ago, and found that it was only items from this domain folder that were causing problems.

Essentially what happens is whenever I use the command 'ask <vendor> to sell <item>', he states that I cannot carry that and promptly drops it on the ground. My first thought was that I was carrying too much, which wasn't the case, since I had an empty inventory.

So, I poked around vendor.c and saw that the vendor uses GetUniqueId() to reference the item its trying to give me.

Alright, so I tried calling the function with a home brew torch of mine, giving it to the NPC using the UniqueId to reference the item, which didn't work, so I tried the same thing with a town item, and of course, it works fine. Here's what it looks like:

Code: [Select]
> call torch->GetUniqueId()
OBJ(torch /domains/Kahara/obj/woodtorch#1215) -> GetUniqueId() = "domains*Kahara*obj*woodtorch*1215"
> give domains*Kahara*obj*woodtorch*1215 to otik
There is no domains*Kahara*obj*woodtorch*1215 here.
> call canteen->GetUniqueId()
OBJ(canteen /domains/town/obj/canteen#1308) -> GetUniqueId() = "domains*town*obj*canteen*1308"
> give domains*town*obj*canteen*1308 to otik
You give Otik a green plastic canteen.


Alright, interesting.

Honestly, the only difference I see is that my domain Kahara, is capitalized, where town is not.

The second problem I'm having with vendors is something with SetValue() and SetBaseCost() and their relation to bargaining.

Currently, if you have an item which uses only SetBaseCost(), buying and selling that item to the vendor seems to ignore the bargaining skill all together.

If you have an item which uses only SetValue(), the shopkeeper exclaims that the item is worthless when you try and sell it to them.

If you use both, bargaining will work, but only for when a player sells to the shop. Also, because of this, when a player has a bargaining skill higher than the shopkeeper, it's actually possible to sell to the shop at a higher price than for the shop to sell back to you, producing an interesting exploit.

14
Dead Souls Support / Peer and Virtual Rooms
« on: December 22, 2008, 06:15:16 AM »
As far as I can tell this should work with no problems or issues. This fixes peer so that it'll work and be happy with virtual rooms.

In /lib/secure/cmds/players/peer.c change

Code: [Select]
/*    if( !unguarded((: file_exists, file + ".c" :)) ||
      (!env = load_object(file)) ) {
        message("my_action", "It is not safe to peer "+str+"!", this_player() );
        return 1;
    }*/

to this

Code: [Select]
    tree = path_file(file);
    parentfile = tree[0];

    if( !unguarded((: file_exists, file + ".c" : )) && !unguarded((: file_exists, parentfile + ".c" :)) ) {
            message("my_action", "It is not safe to peer "+str+"!", this_player() );
            return 1;
    }

    if( !env = load_object(file) ){
            message("my_action", "It is not safe to peer "+str+"!", this_player() );
            return 1;       
    }

Also, don't forget to define 'tree' and 'parentfile.'

The only problem I can see with this is if someone coded a broken exit between two rooms where the file didn't exist, and just so happened that in the same folder, there was a file named the same as the folder.

Anyways, does anyone see something wrong with the logic of this fix?

Just thought I'd propose a fix to peer. :)

15
Dead Souls Support / Missile Weapon
« on: December 18, 2008, 08:32:58 PM »
Alright, I'm not quite sure what I'm doing wrong here. This is my first attempt at anything really new and it was going well, but now I'm on a snag and I can't for the life of me tell why this isn't working.

/lib/lib/missile_weapon.c
Code: [Select]
#include <lib.h>
#include <rounds.h>
#include "include/missile_weapon.h"

inherit LIB_ITEM;

string RequiredMissileType = "arrow";

static void create(){
    item::create();
}

mixed eventShoot(object ob, mixed target){

    object projectile;
   
    if (!projectile = FindProjectile(ob)){
      write("You don't seem to have any ammunition for " + ob->GetShort() + ".");
      return 1;
    }

    this_player()->eventPrint("You notch "+projectile->GetShort()+" and take aim with your "+GetKeyName()+".");
    this_player()->SetAttack(target,(: eventExecute, this_player(), target, ob, projectile :), (target ? ROUND_WEAPON : ROUND_OTHER));

    return 1;

}

mixed FindProjectile(object shooter){

    object item_ammo, bow_ammo;   

    bow_ammo = shooter->GetRequiredMissileType();

    foreach(object thing in all_inventory(environment(shooter))) {
      item_ammo = thing->GetMissileType();
        if (item_ammo == bow_ammo){
          return thing;
        }
      }
}

mixed eventExecute(object attacker, object target, object weapon, object projectile){
    write("Attacker: " + attacker->GetShort());
    write("Defender: " + target>GetShort());
    write("Missile Weapon: " + weapon->GetShort());
    write("Projectile: " + projectile->GetShort() + "\n");

    attacker->eventPrint("You unleash " + projectile->GetShort() + " at " + target->GetName() + ".");
    target->eventPrint(attacker->GetName() + " unleashes " + projectile>GetShort() +" towards you.");
    environment(attacker)->eventPrint(attacker->GetName() + " unleashes " + projectile->GetShort() + " at " + target->GetName() + ".", ({ attacker, target }) );

    return;
}

int SetRequiredMissileType( string str ){ RequiredMissileType = str; return 1; }
string GetRequiredMissileType(){ return RequiredMissileType; }

eventShoot is just called by the shoot.c verb file of course.

This is the result I get...

Attacker...

You notch a pinewood arrow and take aim with your bow.

> Health: [____      ]44% Energy: [__________]100% Stamina: [_______   ]74%
Vjorin sidesteps and pricks you lightly in your right arm with his short sword.
Health: [____      ]43% Energy: [__________]100% Stamina: [_______   ]73%
Vjorin scratches you barely in your torso with his short sword.

Attacker: First Admin Nilrin
Missile Weapon: a bow
Projectile: a pinewood arrow
Defender: Vjorin the Finder of Hidden Chambers

You unleash a pinewood arrow at Vjorin.

Defender...

You completely miss Nilrin.
You prick Nilrin just barely in the torso with your short sword.
You prick Nilrin lightly in the left arm with your short sword.
Nilrin lashes out at your body, but misses it completely.
You scratch Nilrin barely in the right hand with your short sword.
You scratch Nilrin mildly in the right leg with your short sword.
You scratch Nilrin mildly in the left hand with your short sword.
Nilrin swings at you and connects with thin air.

We're missing a line here that should say...
"Nilrin unleashes a pinewood arrow towards you."

Not sure what's going on here.

Pages: [1] 2