Author Topic: Adding No drop/no sell/no give functions to items  (Read 3046 times)

Offline Elohim@ForgottenGods

  • Acquaintance
  • *
  • Posts: 5
    • View Profile
Adding No drop/no sell/no give functions to items
« on: August 01, 2011, 02:05:54 am »
I am working on creating "Soulbound" items to the mud. The builder would inherit LIB_SOULBOUND and add the functions to Set, Get, and Run soulbound items. I have designed SetSoulBound() to accept hard integers 0 and 1 which work great when defined in the create() of the object. But when I try to pass a function through SetSoulBound() nothing happens. So I tried to add a init() function to the inherited soulbound file to be called at the same time as the object's init() file is called at the end of eventMove() but nothing happens.

I'm trying find a way for the function passed in SetSoulBound() to be called after the object is cloned.

Files attached. Please be gentle when criticizing my coding skills or lack thereof!

The files are available for fair use with respective rights intact of contributed works.

soulbound.c (inherited file)
Code: [Select]
/* /lib/props/soulbound.c
  *
  * Libray object to create objects
  * players cannot drop, give away,
  * put in other objects (item
  * smuggling) or sell
  *
  * created on: 20110729
  * created by: Elo'him@ForgottenGods
  */

// Last Modified: 20110731 by Elo

//GetLastEnvironment()


#include <lib.h>
#include <function.h>

inherit LIB_PERSIST;
inherit LIB_OBJECT;


private mixed bound = 0;

// begin abstract methods

varargs SetRetainOnDeath();
varargs SetPreventDrop();
varargs SetPreventGet();
varargs SetPreventPut();
string GetName();
string GetCoreDesc();
string GetExternalDesc();
string GetDefiniteShort();

// end abstract methods


int GetSoulBound(){
if( intp(bound) ) return bound;
if( functionp(bound) ) return evaluate(bound, this_object());

// it's not a func or a int so its no good to us
return 0;
}


void RunSoulBound(){
 
// This can be called directly
//although I couldn't guess as to why

mixed responsePrevent;

responsePrevent = bound?"You cannot do that with the "+
  remove_article((string)GetShort())+"; it is soul bound.":0 ;

SetPreventDrop(responsePrevent);
SetPreventGet(responsePrevent);
SetPreventPut(responsePrevent);
SetRetainOnDeath(bound);

return;
}


int SetSoulBound(mixed val){


if(intp(val)){
bound = val;
RunSoulBound();
return bound;
}

else if( !(functionp(val) & FP_OWNER_DESTED) ) {
bound = evaluate(val, this_object());
RunSoulBound();
return 1;
}

// it's not a func or a int so its no good to us
return 0;   
}


mixed CanSell(){
if (!GetSoulBound()) return 1;

return "You cannot do that with the "
+(string)GetShort()+" because it is a soulbound item.";

return !GetSoulBound();
}

string GetExternalDesc(){

if( GetSoulBound() )
return GetCoreDesc()+"\n%^BOLD%^The "+
GetDefiniteShort()+" is soulbound.%^RESET%^";

else return GetCoreDesc();
}

string array GetSave(){
    return ({ "bound" });
}

static mixed array AddSave(){
return persist::AddSave( ({"bound"}) );
}


void init(){
// no need to make the master soulbound!
write("\n[debug]: "+file_name()+" all clear.");
if(clonep() & functionp(bound)){

bound;
}
}

SimpleShirt.c (file inheriting soulbound.c)
Code: [Select]
/*
gamboised

Body Jerkin
Hands Gloves
Legs Chausses
Feet Ledelsens

Body Tunic
Hands Gloves
Legs Slacks
Feet Boots

Body Separates
Hands Gauntlets
Legs Loincloth
Feet Gaiters
*/
  // override eventDestruct()?
 
 
#include <lib.h>
#include <armor_types.h>
#include <damage_types.h>

inherit LIB_ARMOR;
inherit LIB_SOULBOUND;

int sbItem();
void prsItem();


static void create(){
    armor::create();

    SetKeyName("shirt");
    SetAdjectives( ({"simple"}) );
    SetId( ({ "shirt" }) );
    SetShort( "simple shirt" );
    SetLong(
"gamboised simple shirt"
);
    SetProtection( PHYS_DAMAGE, 1 );
    SetSoulBound( sbItem() );
    SetArmorType( A_SHIRT );
    SetMaterials( ([ "textile" : 100 ]) );
    SetValue( 0 );
    SetMass( 1 );
    }


void init(){
    ::init();

RunSoulBound();
if( clonep() ){ prsItem(); }

}

int sbItem(){

return creatorp(environment());
}

 
void prsItem(){

mapping imapp = ([]);

if(!environment() || !living(environment())) return;

switch ( environment()->GetRace() ) {

case "noor":
imapp = ([
"name" : "tunic",
"adj" : ({"simple"}),
"id" : ({ "tunic","shirt" }),
"short" : "simple tunic",
"long" : "A simple tunic",
]);

break;

case "nuar":
imapp = ([
"name" : "body wraps",
"adj" : ({"simple"}),
"id" : ({ "body wraps","hosen" }),
"short" : "simple body wraps",
"long" : "A simple body wraps",
]);
break;
   
case "vorskh":
imapp = ([
"name" : "jerkin",
"adj" : ({"simple"}),
"id" : ({ "jerkin","shirt" }),
"short" : "simple jerkin",
"long" : "A simple jerkin",
]);
break;

default:
imapp = ([
"name" : "shirt",
"adj" : ({"simple"}),
"id" : ({ "shirt" }),
"short" : "simple shirt",
"long" : "A simple shirt",
]);
}

SetKeyName(imapp["name"]);
SetAdjectives( imapp["adj"] );
SetId( imapp["id"] );
SetShort( imapp["short"] );
SetLong( imapp["long"] );


return;
}

Offline quixadhal

  • BFF
  • ***
  • Posts: 642
    • View Profile
    • WileyMUD
Re: Adding No drop/no sell/no give functions to items
« Reply #1 on: August 01, 2011, 03:42:20 am »
Too early in the morning to actually look at the code, but I wanted to make a comment about the purpose.

It sounds like you're making "cursed" items, rather than what a traditional MMO calls "soulbound".  To a EQ2/WoW/etc player, "soulbound" means you can't give them to any other character in any way, but you can destroy them (via drop) or sell them to an NPC vendor (who will destroy them).

Just making sure everyone's on the same page with respect to what you're attempting to do. :)

Offline cratylus

  • Your favorite and best
  • Administrator
  • ***
  • Posts: 1022
  • Cratylus@Dead Souls <ds> np
    • View Profile
    • About Cratylus
Re: Adding No drop/no sell/no give functions to items
« Reply #2 on: August 01, 2011, 09:41:58 am »
Quote
But when I try to pass a function through SetSoulBound() nothing happens. So I tried to add a init() function to the inherited soulbound file to be called at the same time as the object's init() file is called at the end of eventMove() but nothing happens.

I'm trying find a way for the function passed in SetSoulBound() to be called after the object is cloned.

In a similar vein to quix, I'm actually not going to look at the code until I understand exactly what you're up to.

Tell me if I have this right.

You want certain objects to have modifiable behavior when a command is entered which changes their location.

So if I get the object, a behavior is triggered. Also if I drop it, sell it, etc.

This modifiable behavior would be a function, set on that object in a persistent way.

So when Xur receives his bladed Sceptre of Knifey Power, the NPC that gives it to him can provide it
with some on-drop function such as "always come back after 5 seconds", and Xur can throw his
Sceptre, spike a lame henchman, and have his Sceptre come back.

Or, he the NPC giver could have been grumpy that day and chosen to add a different function,
like "lower HP and be undroppable". And if Xur quits and logs back in, that same arbitrarily-granted
function is still on the Sceptre.

It sounds like you're looking for cursing/enchanting of arbitrary objects in a persistent way.

Is that close to the mark?

-Crat

Offline Nulvect

  • BFF
  • ***
  • Posts: 127
    • View Profile
Re: Adding No drop/no sell/no give functions to items
« Reply #3 on: August 01, 2011, 03:15:20 pm »
To answer your question, you pass function pointers like so:
Code: [Select]
SetSoulbound( (: sbItem :) );

This is not what you need to do here, however. You are adding a new property similar to SetPreventDrop. I suggest you look at /lib/events/drop.c and give.c to see how that's implemented. Your GetExternalDesc looks fine, but you shouldn't need any init()s. If you want soulbound to be available in all armor and weapons, check /lib/std/base_armor.c and /lib/comp/weapon.c (or well, /lib/std/item.c which inherits weapon) to see how other properties are added (and how they persist via GetSave), such as poison.

As for your clothing that changes based on race, this kind of thing is always a pain to do, and doing it via init() will make it change after you buy it, for example. Usually a bad idea.

Offline Elohim@ForgottenGods

  • Acquaintance
  • *
  • Posts: 5
    • View Profile
Re: Adding No drop/no sell/no give functions to items
« Reply #4 on: August 01, 2011, 05:03:42 pm »
Thank you all for your input.

Nulvet: you're definitely right because I took the original function that I had in the shirt and tried turning it into its own inheritable library file. I'll look over the suggested files and see if I can put something together.

Quix: this actually relates to the conversation we had in dchat about trash/recycling bins and call_out(). Without an option to trash the item I see how it looks like a cursed item, lol. My next step was to add a "Are you sure?" check before calling on eventDesctruct() after doing a pending call_out() check.

Crat - Not quite, but going by your example: Xur receives his Sceptre of Knifey Power that is given to him through whichever means; the gods, a treasure chest, finishing a quest, etc. When he receives this absolutely fantastic item he must keep it on his person so that he can't give it to someone less deserving, sell it, drop it on the floor, or hide it in a bag (SetSoulBound( 1 || func())). The powers that be can release this soulbound property (SetSoulBound(0)) to remove this hold.

And to add to my terribleness I couldn't find an edit post option, maybe because I'm still a forum newb? I'll look over the suggestions and post something more presentable. Promise :~)

Offline Elohim@ForgottenGods

  • Acquaintance
  • *
  • Posts: 5
    • View Profile
Re: Adding No drop/no sell/no give functions to items
« Reply #5 on: May 01, 2012, 03:08:25 pm »
I finally reached a solution that works for what i need. It is a full system to prevent any trashing, dropping, etc. with soul bound items in the game. Also a property can be added to storage that the owner can store and only the owner can retrieve the item.  :D


This is the soulbound.c property added to /lib/props/ and defined as LIB_SOULBOUND
Code: [Select]
/* /lib/props/soulbound.c
  *
  * Libray object to create objects
  * players cannot drop, give away,
  * put in other objects (item
  * smuggling) or sell
  *
  * created on: 20110729
  * created by: Elo'him@ForgottenGods
  */

#include <lib.h>
#include <rooms.h>


#ifndef eventMove
inherit LIB_MOVE;
#endif
#ifndef SetDestructOnDrop
inherit LIB_DROP;
#endif
#ifndef GetShort
inherit LIB_DESCRIPTION;
#endif

int __wcount, timer=time();
mixed __bound_;
object __owner;


int eventTrash(){

string *warning = ({
"You try to "+query_verb()+" but hold on for one final time.",
"You try again to "+query_verb()+" it.",
"You attempt to "+query_verb()+" your "+GetShort()+"."
});

if( timer <= time() ){timer = time() + 180; __wcount = sizeof(warning);}
if(!__wcount || (!newbiep(this_player()) && __wcount == 1)){ return 1;}
__wcount--;
write(warning[__wcount]);
return 0;
}

mixed GetOwnerSoulBound(){
if(!undefinedp(__owner)) return __owner;
else return 0;
}
int SetOwnerSoulBound(object ob){
__owner = ob; return 1;
}

mixed GetSoulBound(){
if(!undefinedp(__bound_)) return __bound_;
else return 0;
}

int SetSoulBound(mixed args){

if(!intp(args) && !functionp(args))
error("expected int or function got: "+typeof(args));

__bound_ = args;
return 1;
}

int eventMove(mixed dest){

object owner;
object player = this_player();
string vrb = query_verb();
string obname = GetShort();
string sbmsg = "%^WHITE%^BOLD%^You cannot "+vrb+" your "+
obname+". It is soulbound.%^RESET%^";

if(__bound_){

if(dest->isBag() && dest->GetProperties()["relic_storage"] == owner) return 1;
if(userp(dest) && !undefinedp(__owner) && __owner == player) return 1;

if( member_array(vrb,
({"drop","trash","discard","abandon"})) != -1 && eventTrash()){
SetDestructOnDrop(1);
return ::eventMove(dest);
}

if(functionp(__bound_)){
if(!evaluate(__bound_, player, vrb, dest) ){
write(sbmsg);
return 0;
}
else return ::eventMove(dest);
}

        write(sbmsg);
return 0;
}

 return ::eventMove(dest);
}

And this is the armor that inherits LIB_SOULBOUND
Code: [Select]
//     Created by Elo'him for ForgottenGods 2012
//     Start armor (piece 1 of 6) for new players


#include <lib.h>
#include <armor_types.h>
#include <damage_types.h>

inherit LIB_ARMOR;
inherit LIB_SOULBOUND;

mapping tailored;

void prsItem();
mapping GetConfig();


static void create(){
    armor::create();

    AddSave( ({"tailored"}) );
    SetKeyName("shirt");
    SetAdjectives( ({"simple"}) );
    SetId( ({ "shirt" }) );
    SetShort( "simple shirt" );
    SetLong( "simple shirt" );
    SetArmorType( A_CUSTOM );
SetRestrictLimbs(({"torso","neck","left arm","right arm"}));
    SetMaterials( ([ "leather" : 15, "hemp" : 85 ]) );
    SetValue( 0 );
    SetMass( 1 );
    SetDamagePoints(1600);
    SetProtection( PHYS_DAMAGE, 1 );
}


void init(){
    armor::init();

    if(clonep() && !sizeof(tailored)){ prsItem(); }
}

int ActSoulBound(object player, string vrb, object dest){

    if(!archp(player)) { return 1;}

        return 0;
}

mapping GetConfig(){return tailored;}

void prsItem(){
    string race;

    if(!environment() || !living(environment())) return;
    race = environment()->GetRace();

    if (sizeof(tailored) && tailored["race"] == race) return;

    tailored = ([]);
    tailored["owner"] = geteuid(environment());
tailored["race"] = race;
SetOwnerSoulBound(tailored["owner"]);

    switch ( race ) {

    case "noor":
        tailored["name"] = "tunic";
        tailored["adj"] = ({"simple"});
        tailored["id"] = ({ "tunic","shirt" });
        tailored["short"] = "simple tunic";
        tailored["long"] = "A simple tunic with a leather collar. "
        "Below the collar a lace-up front can be seen to "
        "secure the piece and puffed sleeves billow at each "
        "shoulder capping the tight sleeves right above the "
        "elbow. These sleeves end artfully past the wrist to "
        "ensure warmth when worn with gloves or mitts. The "
        "gray of the tunic has been accented with streaks of "
        "brown and blue in the traditional Noor custom." ;

        break;

    case "nuar":
        tailored["name"] = "body wraps";
        tailored["adj"] = ({"simple"});
        tailored["id"] = ({ "body wraps","wraps","shirt" });
        tailored["short"] = "simple body wraps";
        tailored["long"] = "The body wrap is a long thick strip of "
        "padded leather. Its length is sufficient to wrap around the "
        "torso of the Nuar and over their shoulders. It is accompanied by a "
        "tattered brown toga that is used as a layer of lining to "
        "prevent excessive chafing.";

        break;

    case "vorskh":
        tailored["name"] = "jerkin";
        tailored["adj"] = ({"simple"});
        tailored["id"] = ({ "jerkin","shirt" });
        tailored["short"] = "simple jerkin";
        tailored["long"] = "This jerkin has been made suitable for all "
        "manner of needs an adventuer or traveler alike would face in "
        "the world. The leather of the primary underlayer has been "
        "built to allow maxium protection with little hinderance as per "
        "the Vorskh standard. This main-piece has been reinforced  at "
        "the shoulders with a small pair of epaulets and at the chest "
        "with leather worked from a thick cured hide. The dark blue "
        "leather is accented with artful patterns of burgandy and rust.";

        break;

    default:
        tailored["name"] = "shirt",
        tailored["adj"] = ({"simple"});
        tailored["id"] = ({ "shirt" });
        tailored["short"] = "simple shirt";
        tailored["long"] = "A simple shirt";
    }

    SetKeyName(tailored["name"]);
    SetAdjectives( tailored["adj"] );
    SetId( tailored["id"] );
    SetShort( tailored["short"] );
    SetLong( tailored["long"] );
SetSoulBound((:ActSoulBound:));
    return;
}