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 - Lash

Pages: [1] 2
1
The functions static void RemoveBlindnes() and varargs mixed eventBlind() were not behaving as I expected. A test character was blinded by an npc, however when I tried the RemoveBlindness function as an admin on the test character I received the following output:

Code: [Select]
call test->RemoveBlindness()
The function RemoveBlindness() is not in OBJ(test /secure/save/players/t/test)

Removing the static type (void RemoveBlindness() instead of static void RemoveBlindness()) resulted in the following output when the mud was restarted and the character was blinded:
Code: [Select]
call test->RemoveBlindness()
OBJ(test /secure/save/players/t/test) -> RemoveBlindness() = 0

The character had vision restored. If the character was NOT blinded and the admin used the call test->RemoveBlindness() call the following output was received:

Code: [Select]
*Tried to take a member of something that isn't a class.
Object: /secure/save/players/t/test (/lib/genetics.c) at line 53

'<fake>' at /secure/save/creators/l/lash (/<driver>) at line 0
'cmdAll' at /secure/save/creators/l/lash (/lib/command.c) at line 231
'cmd' at /secure/cmds/creators/call at line 37
'CATCH' at /secure/cmds/creators/call at line 37
'<fake>' at /secure/cmds/creators/call (/<driver>) at line 0
'RemoveBlindness' at /secure/save/players/t/test (/lib/genetics.c) at line 53
Trace written to /log/catch
OBJ(test /secure/save/players/t/test) -> RemoveBlindness()
Error in execution: *Tried to take a member of something that isn't a class.

Well, that makes sense - the character is not blinded so the class object is not present. The RemoveBlindness function is now
working as expected (I think).

In eventBlind() the messages weren't being returned correctly. Here's the code (called from an npc) that was used to blind the character:
Code: [Select]
void blindness(object ob){

    object target = ob->GetCurrentEnemy();
    object env = environment(ob);
    int save = target->GetMagicResistance();
    int s_throw = random(99)+1;

    if(target->GetBlind()){
    return;
    }
    if (s_throw <= save){
        tell_room(env, "\n"+capitalize(target->GetShort())+" resists "+ob->GetKeyName()+"'s magic attack!\n", ({ ob, target }));
        tell_object(target, "\n%^BOLD%^%^YELLOW%^You resist being blinded by "+ob->GetKeyName()+"!%^RESET%^\n");
        target->eventTrainSkill("magic defense",save,s_throw,1);
        return;
    }
    else{
    target->eventBlind(target, 100, ({"\n%^BOLD%^%^GREEN%^You have been blinded!%^RESET%^\n","\n%^BOLD%^%^YELLOW%^You can see again!%^RESET%^\n"}) );
    tell_room(env, "\n"+capitalize(target->GetShort())+" seems to be blinded!\n", ({ob, target}));
    }
}

Looking at varargs mixed eventBlind() and comparing it to the RemoveBlindness function, it seemed like something was amiss so I did this:
Code: [Select]
varargs mixed eventBlind(object who, int amt, mixed end){
    Blind = new(class blindness);
    Blind->count = amt;
    Blind->end = end;
   /*following code added as seen in RemoveBlindness() function*/
    if( arrayp(end) ){
        send_messages(end[1], end[0], this_object());
        tell_player(who, "end[0] is "+end[0]+"and end[1] is "+end[1]);
    }
    else if( functionp(end) && !(functionp(end) & FP_OWNER_DESTED) ){
        evaluate(end, this_object());
    }
    else{
    tell_player(who,"You have been blinded!");
    }
    /*end addition*/
    return 1;
}

The messages are being returned correctly as seen by the output:
Code: [Select]
You have been blinded!

end[0] is
You have been blinded!
and end[1] is
You can see again!

After the 100 tick timer counts down (coded in the npc eventBlind() function) the characters vision is restored and the correct "You can see again!" message is displayed.

But hold on!

Why does this from eventBlind()
Code: [Select]
send_messages(end[1], end[0], this_object());

result in the correct order of the messages displayed? I would have thought the 2nd element of the array end[1] would have been
displayed first! Instead, it seems like it's backwards! But it works as expected...

Also, if I use this command as an admin:
Code: [Select]
call test->eventBlind("secure/save/players/t/test",10,({"you are blind","you can now see"}))

the test character is blinded for 10 ticks and the correct messaging is displayed.

Anyway, just some observations I noticed that I thought I would share. If anyone has more insight to this, or made other changes,
your input would be aprreciated.

2
Bug Central / Saving armor conditions
« on: February 24, 2016, 02:02:24 PM »
I noticed in the default 3.8.6 base mudlib that armor conditions (wear and tear on armor after comabat, for example) were not being saved. A player could quit, log back in, and have all new untainted armor.

Two files were changed so armor conditions are retained:

As pointed out by Quixadhal on dschat the DamagePoints variable in lib/lib/props/deterioration.c was not initialized and thus not being saved:
Code: [Select]
private int DamagePoints;

changed to

private int DamagePoints    = 0;


In /lib/lib/std/base_armor.c the variable Protection was added to the my_save variable like so:
Code: [Select]
my_save = equip::GetSave() + value::GetSave() + mass::GetSave() +
        poison::GetSave() + deterioration::GetSave();

changed to:

my_save = equip::GetSave() + value::GetSave() + mass::GetSave() +
        poison::GetSave() + deterioration::GetSave() + ( ({"Protection"}) );

So far as I can tell, the armor objects are being saved with the correct values for deterioration, DamagePoints, and "cuts" and "dents" values as defined in the GetItemCondition() function in the base_armor.c file after character logout/login.

As a check I added the following bit of code to deterioration.c to make sure the "current" DamagePoints was being reported vs the MaxDamagePoints assigned to the specific armor object:

Code: [Select]
int GetMaxDamagePoints(){
    return MaxDamagePoints;
}

Here's some example output for armor worn by a test character that has been damaged after being attacked by an npc with a "blade" type weapon. The test character quit and logged back in. The admin character evaluated the armor object as follows:

Code: [Select]
call leggings->GetDamagePoints()
OBJ(iron leggings /domains/diku-alfa/room/30.zon/armor/3092_leggings_iron#101)
-> GetDamagePoints() = 22

call leggings->GetMaxDamagePoints()
OBJ(iron leggings /domains/diku-alfa/room/30.zon/armor/3092_leggings_iron#101)
-> GetMaxDamagePoints() = 81

call leggings->GetItemCondition()

IN GETITEM CONDITION
cuts is 80
dents is 100
cuts is 80
OBJ(iron leggings /domains/diku-alfa/room/30.zon/armor/3092_leggings_iron#101)
-> GetItemCondition() = "Its surface is scratched and unmarred."

Without making the two changes as described above none of the above values were being saved.

*note - added some output code to base_armor.c to make sure the "cuts" and "dents" variables were being reported accurately.

3
Code Vault / Code for character skill based leveling
« on: January 30, 2016, 11:58:21 AM »
I've tested this out on a new Dead Souls installation and a running mud. Only tested low level test characters so not sure how this will work at character levels above 20.

Here's my modded code changes for player based skill selection and advancement. I'm not going to post the complete files for all this so as not to spam the post too much. Line numbers are for Dead Souls 3.8.6 default base mudlib.

An alternative to having players select their character skills during their first log in is to have an NPC assign skills to players after entering the game using the SetSkill() function. But, for skill based advancement vs. XP based advancing the below files can be added.

Configuration Files:
note: CLASS_SELECTION and SKILL_SELECTION should not both be set to 1!

config.h in /lib/secure/include

Code: [Select]
//line 44
#define CLASS_SELECTION          0
#define SKILL_SELECTION          1
#define XP_ADVANCE               0
#define SKILL_ADVANCE            1

cfg.h in lib/secure/include
Code: [Select]
//line 19
#define CFG_SKILLS DIR_SECURE_CFG "/skills.cfg"

skills.cfg (file) in /lib/secure/cfg

Code: [Select]
#/lib/secure/cfg/skills.c
#list of available skills
bargaining
blade attack
blade defense
blunt attack
blunt defense
concealment
conjuring
detection
faith
fishing
healing
knife attack
knife defense
magic attack
magic defense
melee attack
melee defense
multi-hand
multi-weapon
projectile attack
projectile defense
psionic attack
psionic defense
psionic detection
stealing
stealth
murder

Added mudconfig commands to change whether classes with experience point advancement are used or not, or skills chosen on player creation and
advancement based on skill point leveling. 

mudconfig.c in lib/secure/cmds/admin

Code: [Select]
//line 23
string array modals = antimodals + ({ "channelpipes", "fastcombat",
        "catchtell","matchcommand", "matchobject", "autowiz", "locked",
        "localtime", "justenglish", "justhumans", "encumbrance", "pk",
        "compat", "exitsbare", "nmexits", "grid", "minimap", "wizmap",
        "cgi", "dirlist", "creweb", "selectclass", "selectskills",
        "xpadvancement", "skilladvancement", "severable", "retain",
        "defaultparse", "disablereboot", "loglocal", "logremote",
        "questrequired", "autoadvance","guestallowed", "playerintertell" });

//line 574
        case "selectclass" : which = "CLASS_SELECTION";break;
        case "selectskills" : which = "SKILL_SELECTION";break;
        case "xpadvancement" : which = "XP_ADVANCE";break;
        case "skilladvancement" : which = "SKILL_ADVANCE";break;
       
//line 629
       if(which == "CLASS_SELECTION" || which == "SKILL_SELECTION"
       || which =="XP_ADVANCE" || which == "SKILL_ADVANCE")
        write("To make this configuration take effect, reboot the mud.\n"+
                "It might be a good idea to disable the other SELECTION and/or\n"+
                "ADVANCE options before reboot.");

//line 1007 in string(GetHelp())
            "\nmudconfig selectclass [ yes | no ] (whether new players "
            "choose a class on login)"
            "\nmudconfig selectskills [yes | no ] (whether new players "
            "select their own skill set on login (classless mud)"
            "\nmudconfig xpadvancement [yes | no ] (whether players gain levels "
            "using the experience point system or not"
            "\nmudconfig skilladvancement [yes | no ] (whether players gain levels "
            "by increasing their skill points or not"

Connection File:
Note: for a virgin installation rename this to connect.real; if the mud is up and running replace the connect.c file and reboot. To change the amount of skills a character chooses, change the values in the if(sizeof(?tmp) < x) expression in the CoomandPick(whatever)Skills() functions.

Help files for skills not written yet so code is commented out for  cmdHelpSkills(args) function.
Not good at daisy-chaining input_to() so this spam is the result:

connect.c in /lib/secure/lib

Code: [Select]
//FUNCTION PROTOTYPES

static private string *Skills;
static private string *tskills = ({});
static private string *ptmp = ({});
static private string *stmp = ({});
static private string *mtmp = ({});
static void eventSkillSelectionInstructions();
static void eventSelectPrimarySkills();
static void InputPrimarySkills(string str);
static void cmdPickPrimarySkills(string args);
static void eventSelectSecondarySkills();
static void InputSecondarySkills(string str);
static void cmdPickSecondarySkills(string args);
static void eventSelectMinorSkills();
static void InputMinorSkills(string str);
static void cmdPickMinorSkills(string args);
static void cmdHelpSkills(string args);
static private string *GetSkills();

//CODE BLOCKS

//line 698
if(!AUTO_WIZ){
        if(CLASS_SELECTION){
            eventSelectClass();
            return;
        }           
        if(SKILL_SELECTION){
            eventSkillSelectionInstructions();
            //eventSelectPrimarySkills();
            return;
        }
        eventCompleteChar();
    }
//line 818
static void eventSkillSelectionInstructions(){
    receive("\n\nYou must now choose the skill sets that will define your character.\n"
            "Available are:\n"
            "    5 PRIMARY skill slots\n"
            "    3 SECONDARY skill slots\n"
            "    5 MINOR skill slots\n\n"
            "The more you use a skill the faster your level will increase.\n"
            "Think about your character concept and choose wisely.\n");
    receive("\nDuring skill selection you will be presented with a list of available\n"
            "skills, as well as a list of skills you have already chosen. The\n"
            "following commands will be available to you:\n\n");
    receive("\thelp - shows the help file for SKILLS\n"
            "\tpick SKILL - pick a particular SKILL for yourself\n\n"
            "Press return to begin skill selection.");
    input_to((: eventSelectPrimarySkills :), I_NOESC);
}

static void eventSelectPrimarySkills(){
    int x = sizeof(ptmp);
    string rskills = format_page(sort_array(Skills, 1), 4);
    //string cpskills = format_page(sort_array(ptmp, 1), 1);
    string cpskills = format_page(sort_array(ptmp, 1), 3);
               
    receive("\n\nYou must now pick "+(5-x)+" PRIMARY Skills.\n");
    receive("\nYou have chosen the following PRIMARY Skills:\n");
    receive(cpskills);
    receive("\nValid skills to choose from:\n\n");
    receive(rskills);
    receive("\nSkill: \n");
    input_to((: InputPrimarySkills :), I_NOESC);
}

static void InputPrimarySkills(string str){
    string cmd, args, argse;
    string *tmp = Skills;
    string rskills = format_page(sort_array(Skills, 1), 4);

    if( str == "" || !str ) {
        receive("\nYou must pick a Skill: ");
        input_to((: InputPrimarySkills :), I_NOESC);
        return;
    }
    if( sscanf(str, "%s %s", cmd, args) != 2){
        cmd = str;
        args = 0;
    }
    if( sscanf(str, "%s %s", cmd, args) == 2){
        if(cmd != "pick"){
            cmd = str;
            args = 0;
        }
    }
    if(member_array(cmd, tmp) != -1){
        args = cmd;
        cmd = "pick";
    }
    switch(cmd) {
        /*case "help":
            cmdHelpSkills(args);
        return;*/

        case "pick":
            cmdPickPrimarySkills(args);
        return;

        default:
        receive("Invalid command or no such skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputPrimarySkills :), I_NOESC);
        return;
    }
}

static void cmdPickPrimarySkills(string args) {
    string *tmp = Skills;
    string str;
    string rskills = format_page(sort_array(Skills, 1), 4);
       
    if( !args || args == "" ) {
        receive("You must specify a skill to pick. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputPrimarySkills :), I_NOESC);
        return;
    }
    if( member_array(args, tmp)  == -1 ) {
        receive("No such Skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputPrimarySkills :), I_NOESC);
        return;
    }
    if(sizeof(ptmp) < 5){
        ptmp+=({args});
        Skills -=({args});
        if(sizeof(ptmp) == 5){
            eventSelectSecondarySkills();
        }else{ eventSelectPrimarySkills();
        }
    }
}

static void eventSelectSecondarySkills(){
    int x = sizeof(stmp);
    string rskills = format_page(sort_array(Skills, 1), 4);
    string cpskills = format_page(sort_array(ptmp, 1), 3);
    string csskills = format_page(sort_array(stmp, 1), 3);
         
    receive("\nYou must now pick "+(3-x)+" SECONDARY Skills.\n");
    receive("\nYou have chosen the following PRIMARY Skills:\n");
    receive(cpskills);
    receive("\nYou have chosen the following SECONDARY Skills:\n");
    receive(csskills);
    receive("\nValid skills to choose from:\n\n");
    receive(rskills);
    receive("\nSkill: \n");
    input_to((: InputSecondarySkills :), I_NOESC);
}

static void InputSecondarySkills(string str){
    string cmd, args, argse;
    string *tmp = Skills;
    string rskills = format_page(sort_array(Skills, 1), 4);

    if( str == "" || !str ) {
        receive("\nYou must pick a Skill: ");
        input_to((: InputSecondarySkills :), I_NOESC);
        return;
    }
    if( sscanf(str, "%s %s", cmd, args) != 2){
        cmd = str;
        args = 0;
    }
    if( sscanf(str, "%s %s", cmd, args) == 2){
        if(cmd != "pick"){
            cmd = str;
            args = 0;
        }
    }
    if(member_array(cmd, tmp) != -1){
        args = cmd;
        cmd = "pick";
    }
    switch(cmd) {
        /*case "help":
            cmdHelpSkills(args);
        return;*/

        case "pick":
            cmdPickSecondarySkills(args);
        return;

        default:
        receive("Invalid command or no such skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputSecondarySkills :), I_NOESC);
        return;
    }
}

static void cmdPickSecondarySkills(string args) {
    string *tmp = Skills;
    string str;
    string rskills = format_page(sort_array(Skills, 1), 4);

    if( !args || args == "" ) {
        receive("You must specify a skill to pick. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputSecondarySkills :), I_NOESC);
        return;
    }
    if( member_array(args, tmp)  == -1 ) {
        receive("No such Skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputSecondarySkills :), I_NOESC);
        return;
    }
    if(sizeof(stmp) < 3){
        stmp+=({args});
        Skills -=({args});
        if(sizeof(stmp) == 3){
            eventSelectMinorSkills();
        }else{ eventSelectSecondarySkills();
        }
    }
}

static void eventSelectMinorSkills(){
    int x = sizeof(mtmp);
    string rskills = format_page(sort_array(Skills, 1), 4);
    string cpskills = format_page(sort_array(ptmp, 1), 3);
    string csskills = format_page(sort_array(stmp, 1), 3);
    string cmskills = format_page(sort_array(mtmp, 1), 3);   

    receive("\n\nYou must now pick "+(5-x)+" MINOR Skills.\n");
    receive("\nYou have chosen the following PRIMARY Skills:\n");
    receive(cpskills);
    receive("\nYou have chosen the following SECONDARY Skills:\n");
    receive(csskills);
    receive("\nYou have chosen the following MINOR Skills:\n");
    receive(cmskills);
    receive("\nValid skills to choose from:\n\n");
    receive(rskills);
    receive("\nSkill: \n");
    input_to((: InputMinorSkills :), I_NOESC);
}

static void InputMinorSkills(string str){
    string cmd, args, argse;
    string *tmp = Skills;
    string rskills = format_page(sort_array(Skills, 1), 4);
   
    if( str == "" || !str ) {
        receive("\nYou must pick a Skill: ");
        input_to((: InputSecondarySkills :), I_NOESC);
        return;
    }
    if( sscanf(str, "%s %s", cmd, args) != 2){
        cmd = str;
        args = 0;
    }
    if( sscanf(str, "%s %s", cmd, args) == 2){
        if(cmd != "pick"){
            cmd = str;
            args = 0;
        }
    }
    if(member_array(cmd, tmp) != -1){
        args = cmd;
        cmd = "pick";
    }
    switch(cmd) {
        /*case "help":
            cmdHelpSkills(args);
        return;*/

        case "pick":
            cmdPickMinorSkills(args);
        return;

        default:
        receive("Invalid command or no such skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputMinorSkills :), I_NOESC);
        return;
    }
}

static void cmdPickMinorSkills(string args) {
    string *tmp = Skills;
    string str;
    string rskills = format_page(sort_array(Skills, 1), 4);
       
    if( !args || args == "" ) {
        receive("You must specify a skill to pick. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputMinorSkills :), I_NOESC);
        return;
    }
    if( member_array(args, tmp)  == -1 ) {
        receive("No such Skill. Valid skills to choose from:\n");
        receive(rskills);
        receive("\nSkill: \n");
        input_to((: InputMinorSkills :), I_NOESC);
        return;
    }
    if(sizeof(mtmp) < 5){
        mtmp+=({args});
        Skills -=({args});
        if(sizeof(mtmp) == 5){
            eventCompleteChar();
        }else{ eventSelectMinorSkills();
        }
    }


/*static void cmdHelpSkills(){
}*/

static private string *GetSkills(){
    string *ret;
    ret = Skills;
    return ret;
}

//line 900 in eventCompleteChar()
    if(ptmp){
        string str;
        receive("\nCharacter Summary\n");
        receive("-------------------------\n");
        receive("PRIMARY Skills picked:\n");
        foreach(str in ptmp){
            receive("'"+str+"' ");
            Player->AddSkill(str,1,1);
        }
        receive("\n");
    }
    if(stmp){
        string str;
        receive("\nSECONDARY Skills picked:\n");
        foreach(str in stmp){
            receive("'"+str+"' ");
            Player->AddSkill(str,2,1);
        }
        receive("\n");
    }
    if(mtmp){
        string str;
        receive("\nMINOR Skills picked:\n");
        foreach(str in mtmp){
            receive("'"+str+"' ");
            Player->AddSkill(str,3,1);
        }
        receive("\n");
    }
    this_player()->SetTerminal("ansi");
    PLAYERS_D->AddPlayerInfo(Name);
    call_out( (: eventCre, Name :), 60);
    receive("\nIf you wish to create a different character,\n"
            "use the 'suicide' command after logging in and\n"
            "start over. Have FUN!\n"
            "Press <return> within 60 seconds to continue.");     
    input_to((: eventEnterGame :), I_NOESC);
}

Leveling files for skill based leveling:

Used for collecting player data used in the skill leveling process for players.c

abilities.c in /lib/lib/lvs

Code: [Select]
//FUNCTION PROTOTYPES
private string array GetPrimarySkills();
private string array GetSecondarySkills();
private string array GetMinorSkills();

//CODE BLOCKS
//line 120
string array GetPrimarySkills(){
    return filter(keys(Skills), (: Skills[$1]["class"] == 1 :));
}

string array GetSecondarySkills(){
    return filter(keys(Skills), (: Skills[$1]["class"] == 2 :));
}

string array GetMinorSkills(){
    return filter(keys(Skills), (: Skills[$1]["class"] == 3 :));
}

Check to see if 3 primary skills, 2 secondary skills, and 1 minor skill are at max levels for the character at their current player level. If so, advance the player level. Has checks for SKILL_ADVANCE and XP_ADVANCE variables from config.h

players.c in /lib/secure/daemon
Code: [Select]
//line 306
int CheckAdvance(object ob){
    string str;
    string *ptmp = ({});
    string *stmp = ({});
    string *mtmp = ({});
    int dlev, x ,y ,z, xp, qp;
    int *plevels = ({});
    int *slevels = ({});
    int *mlevels = ({});
    int *psorted, *ssorted, *msorted;
   
    dlev = (ob->GetLevel())+1;
    /* get and sort primary skills and levels */
    if(SKILL_ADVANCE){
        ptmp = this_player()->GetPrimarySkills();
        foreach(str in ptmp){
            plevels += ({this_player()->GetSkillLevel(str)});
        }
        plevels = sort_array(plevels,-1);
        x = this_player()->GetMaxSkillLevel(ptmp[0]);
       
        /* get and sort secondary skills and levels */
        stmp = this_player()->GetSecondarySkills();
        foreach(str in stmp){
            slevels += ({this_player()->GetSkillLevel(str)});
        }   
        slevels = sort_array(slevels,-1);
        y = this_player()->GetMaxSkillLevel(stmp[0]);
       
        /* get and sort minor skills and levels */
        mtmp = this_player()->GetMinorSkills();
        foreach(str in mtmp){
            mlevels += ({this_player()->GetSkillLevel(str)});
        }
        mlevels = sort_array(mlevels,-1);
        z = this_player()->GetMaxSkillLevel(mtmp[0]);
       
        if(plevels[0] >= x && plevels[1] >= x && plevels[2] >=x
           && slevels[0] >=y && slevels[1] >= y
           && mlevels[0] >= z){
            if(AUTO_ADVANCE) AutoAdvance(ob, dlev);
            return 1;
        }
    return 0;
    }
    if(XP_ADVANCE){
        if(!ob || !playerp(ob)) return 0;
        if(!sizeof(Levels)) CompileLevelList();
        if(!Levels[dlev]) return 0;
        xp = ob->GetExperiencePoints();
        qp = ob->GetQuestPoints();
        if(xp >= Levels[dlev]["xp"] && qp >= Levels[dlev]["qp"]){
            if(AUTO_ADVANCE) AutoAdvance(ob, dlev);
            return 1;
        }
    }
    return 0;
}

Errors thrown for AddChannel() function when player logs into the mud:

/lib/player.c

Code: [Select]
//line 318 in int SetUP()
if( !GetClass() && CLASS_SELECTION ) SetClass("explorer");

//line 357
if( GetClass() ) AddChannel(GetClass());

Adding custom points when player attains a new level (optional).

genetics.c in /lib/lib

Code: [Select]
//line 42
void AddCustomizationPoints(){
     int x, y, z;
     string *stats;
     string str;

     x = this_player()->GetCustomStats(); //don't blow away players custom stat points if not used
       
     stats = this_player()->GetStats();
        foreach(str in stats){
            x += this_player()->GetBaseStatLevel(str);
            y++;
        }
        write("x is "+x+" y is "+y+"\n");
        if(x <= ((y*100)-15)){ //stat points not greater than 100
            z = random(15)+1;
            write("z is "+z+"\n");
        }else{ z = random(x)+1;
        }
     Custom = ([ "stats" : z, "deviations" : 0, "deviating" : 0, ]);
}

Where customization points get added upon leveling:

level.c in /lib/lib/lvs

Code: [Select]
//line 45
if(XP_ADVANCE){
        foreach(mixed key, mixed val in skills){
            if(skills[key]["class"] > 3) skills[key]["class"] = 4;
            subject->SetSkill(key,
                    skills[key]["level"] + moduli[skills[key]["class"]],
                    skills[key]["class"]);
        }

        foreach(mixed key, mixed val in stats){
            if(stats[key]["class"] > 3) stats[key]["class"] = 4;
            subject->SetStat(key,
                    stats[key]["level"] + moduli[stats[key]["class"]],
                    stats[key]["class"]);
        }

        subject->SetLevel(desired_level);

        if(interactive() && find_object(INSTANCES_D)){
            INSTANCES_D->SendWhoUpdate(this_object()->GetKeyName());
        }
    }

//line 65
if(SKILL_ADVANCE){
        subject->AddCustomizationPoints();
        subject->SetLevel(desired_level);

        if(interactive() && find_object(INSTANCES_D)){
            INSTANCES_D->SendWhoUpdate(this_object()->GetKeyName());
        }
    }

4
I'm a fan of Bethesda Softworks Elder Scrolls series, especially Daggerfall, where character advancement is based on skill use. I wanted to incorporate something similar into Dead Souls. Below is my design strategy and I'm looking for suggestions and comments from the experienced Admins out there:

When logging in (/lib/secure/lib/connect.c) the player is able to choose 5 primary, 3 secondary, and 5 minor skills from the current base Dead Souls skill selections - more skill selections can be added later as needed. This allows for a decent degree of player customization for their character. For example, mage types can be proficient at  blade attack as well as use magic,  etc.

Leveling is based on increasing skill levels. The more the player uses the skill the faster they will level. Currently, I have leveling increases occurring when the player reaches their maximum skill levels in 3 of the primary, 2 of the secondary, and 1 of the minor skills. Along with training points, I also wanted to add a random number of customization points. Stats will cap out at 100 and no more customization points will be allotted when leveling.

I've looked at /lib/lvs/abilities.c and have decided not to mess with the way skills and skill points are increased and alloted so far. Of course the fun part will be creating new lib or domain files that are used for training ( using eventTrain() ) and creating and adding new skills! 

Just wondering if anybody has done this and has some suggestions. I'm in the finalization part on this mod and will post the code when finished, but now it is at the point where making modifications may be relatively easy. Also the mud will be able to be configured for incorporating skill based or class based character configuration, as well as skill based or XP based character leveling.

Thanks in advance for comments and suggestions...

Below is the output for character creation after a player has chosen their primary, secondary, and 4 of 5 minor skills (text displayed is shown before the character enters the game):

You must now pick 1 MINOR Skills.
Picking these skills determines how your character will increase in levels.The
more you use a skill the faster your level will increase.

You may issue the following commands:
         help - shows the help file for SKILLS
         pick SKILL - pick a particular SKILL for yourself

You have chosen the following PRIMARY Skills:
blade attack                                                                   
blunt attack                                                                   
knife attack                                                                   
magic attack                                                                   
projectile attack                                                               

You have chosen the following SECONDARY Skills:
conjuring                                                                       
faith                                                                           
healing                                                                         

You have chosen the following MINOR Skills:
bargaining                                                                     
fishing                                                                         
stealing                                                                       
stealth                                                                         

   Valid skills:

blade defense             blunt defense             concealment               
detection                    knife defense             magic defense             
melee attack              melee defense           multi-hand               
multi-weapon             murder                      projectile defense       
psionic attack             psionic defense          psionic detection         

Skill:
detection

PRIMARY Skills picked:
magic attack
blade attack
knife attack
projectile attack
blunt attack

SECONDARY Skills picked:
conjuring
healing
faith

MINOR Skills picked:
bargaining
stealth
stealing
fishing
detection

If you wish to create a different character use the command 'suicide' after
logging in and start over. Have FUN!

5
Design Lab / Bonuses, bitvectors, and resistance, oh my!
« on: December 28, 2015, 01:26:57 PM »
I wanted to add a function to /lib/bonus that will set a resistance type in a player for a limited time:

bonus.c
Code: [Select]
/* /lib/bonus.c
 * from the dead souls mudlib http://www.dead-souls.net
 *
 * modified by Lash (ccoker) for use in The Brass Ring
 * bug fix: bonus object wasn't being destructed
 * 2015-12-28 added functionality for setting resistance
 */

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

inherit LIB_ITEM;

int SetBonuses();

mapping Skills = ([]);
mapping Stats = ([]);
mapping Points = ([]);

int Duration = 15;
string bonusname;
string brl = " "; //bonusresistance level
int brt = 0;      //bonusresistance type

void create(){
    item::create();
    AddSave( ({ "Skills", "Stats", "Points", "Duration", "bonusname" }) );
    SetInvis(1);
    SetId("bonus_object");
    SetShort("bonus");
    SetLong("A bonus");
}

void init(){
    item::init();
    set_heart_beat(1);
    if(environment(this_object()) && living(environment(this_object())))
        SetBonuses();
}

void heart_beat(){
    if(Duration) Duration--;
    /* modified by Lash - bonus object wasn't being destructed */
    else this_object()->eventDestruct();
    /* end mod */
}

mapping SetStats(mapping arg){
    Stats = copy(arg);
    if(environment(this_object()) && living(environment(this_object())))
        SetBonuses();
    return copy(Stats);
}

mapping GetStats(){
    return copy(Stats);
}

mapping SetSkills(mapping arg){
    Skills = copy(arg);
    return copy(Skills);
}

mapping GetSkills(){
    return copy(Skills);
}

mapping SetPoints(mapping arg){
    Points = copy(arg);
    return copy(Points);
}

mapping GetPoints(){
    return copy(Points);
}

int SetBonusDuration(int i){
    Duration = i;
}

int AddBonusDuration(int i){
    Duration += i;
    if(Duration < 0) Duration = 0;
    return Duration;
}

int GetBonusDuration(){
    return Duration;
}

int SetBonuses(){
    object env = environment();
    if(!env || ! living(env)) return 0;
    if(sizeof(Stats))
        foreach(string key, int val in Stats){
            env->AddStatBonus(key, val);
        }
    if(sizeof(Skills))
        foreach(string key, int val in Skills){
            env->AddSkillBonus(key, val);
        }
    if(sizeof(Points))
        foreach(string key, int val in Points){
            switch(key){
                case "HP" : env->AddHP(val);break;
                case "XP" : env->AddExperiencePoints(val);break;
                case "SP" : env->AddStaminaPoints(val);break;
                case "MP" : env->AddMagicPoints(val);break;
                case "poison" : env->AddPoison(val);break;
                case "caffeine" : env->AddCaffeine(val);break;
                case "food" : env->AddFood(val);break;
                case "drink" : env->AddDrink(val);break;
                default : break;
            }
        }
    env->SetResistance(brt,brl);
         
    return 1;
}

int RemoveBonuses(){
    object env = environment();
    if(!env || ! living(env)) return 0;
    if(sizeof(Stats))
        foreach(string key, int val in Stats){
            env->RemoveStatBonus(key);
        }
    if(sizeof(Skills))
        foreach(string key, int val in Skills){
            env->RemoveSkillBonus(key);
        }
    env->SetResistance(brt,"none");
       
    return 1;
}

int eventDestruct(){
    if(!valid_event(previous_object(), this_object())) return 0;
    RemoveBonuses();
    this_object()->eventMove(ROOM_FURNACE);
    return ::eventDestruct();
}

string GetBonusName(){
    return bonusname;
}

string SetBonusName(string name){
    return bonusname = name;
}

varargs string SetMagicResistance(int type, string level){
    brt = type;
    brl = level;
}

mixed CanGet(object who){ return 0; }
mixed CanGive(object who){ return 0; }
mixed CanSell(object who){ return 0; }
mixed CanDrop(object who){ return 0; }
mixed CanPut(object who){ return 0; }

potion.c
Code: [Select]
/* 2015-12-28
 * modified by ccoker to add functionality for resistance
 */
#include <lib.h>
#include <damage_types.h>

inherit LIB_MEAL;

mapping Skills = ([]);
mapping Stats = ([]);
mapping Points = ([]);
string brl = " ";
int brt = 0;

int Duration;

void create(){
    meal::create();
}

mapping SetStats(mapping arg){
    Stats = copy(arg);
    return copy(Stats);
}

mapping GetStats(){
    return copy(Stats);
}

mapping SetSkills(mapping arg){
    Skills = copy(arg);
    return copy(Skills);
}

mapping GetSkills(){
    return copy(Skills);
}

mapping SetPoints(mapping arg){
    Points = copy(arg);
    return copy(Points);
}

mapping GetPoints(){
    return copy(Points);
}

int SetDuration(int i){
    Duration = i;
    return Duration;
}

int GetDuration(){
    return Duration;
}

varargs string SetMagicResistance(int type, string level){
    brt = type;
    brl = level;
}

mixed eventDrink(object who){
    object ob=new(LIB_BONUS);
    ob->SetPoints(Points);
    ob->SetStats(Stats);
    ob->SetSkills(Skills);
    ob->SetBonusDuration(Duration);
    ob->SetMagicResistance(brt,brl);
    ob->eventMove(who);
    return meal::eventDrink(who);
}

mixed eventEat(object who){
    object ob=new(LIB_BONUS);
    ob->SetPoints(Points);
    ob->SetStats(Stats);
    ob->SetSkills(Skills);
    ob->SetBonusDuration(Duration);
    ob->SetMagicResistance(brt,brl);
    ob->eventMove(who);
    return meal::eventEat(who);
}

Example potion - sets resistance to ALL_EXTERNAL_DAMAGE for 300 heart_beats
Code: [Select]
/*
 * Modified by Lash (Christopher Coker) for use with:
 *
 * The Dead Souls Mud Library version 2
 * developed by Cratylus
 * http://www.dead-souls.net
 */

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

inherit LIB_POTION;

static void create() {
    potion::create();
    SetKeyName("potion");
    SetId( ({ "potion","philtrum","purple" }) );
    SetShort("a purple potion");
    SetLong("It looks rather strange!");
    SetMass(1);
    SetMealType(MEAL_DRINK);
    SetStrength(0);
    SetBaseCost("gold",500);
    SetNoCondition(1);
    SetBonusResistance(ALL_EXTERNAL_DAMAGE,"medium");
    SetDuration(300);
    SetProperty("magic", "This potion cures confers the spell 'sanctuary' to the imbiber.");
}

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

So far it seems to work. The bitvectors are being moved around correctly as defined in genetics.c and resistance to all external damage (x/2 in eventReceiveDamage() in body.c) is being processed correctly.

The drawback (maybe?!) is that other Resistance bits have the potential to be wiped out once the bonus object is destructed.

(modified to rename functions)

6
Design Lab / Timed events for player characters
« on: December 22, 2015, 06:05:15 AM »
I'm looking at adding functions to support "factions" in a mud as a sort of replacement for the idea of having guilds. A timestamp is set in the player characters file whenever the PC interacts with a npc faction member, linked to a reputation (with that faction) level, in a mapping variable called Factions. The idea is that if the PC does not interact with a faction member in the course of 30 mud days their reputation decreases by 1. So this is how I have set up so far:

In the following function  "timestamps" are set in the player character using the current mudtime as defined by SEASONS_D->GetTime().

File name is factions.c:

Code: [Select]
mapping Factions = ([]);

mixed AddFaction(string fac){

    int flev = 1;
    int ltime = SEASONS_D->GetTime();
    int rlev = 1;
    int rtime = SEASONS_D->GetTime();
   
    if( !stringp(fac) ) return 0;
    if( Factions[fac] ) return 0;
    Factions[fac] = (["faction level":flev,"level_timer":ltime,"reputation":rlev,
                      "reputation_timer": rtime ]);
    return Factions[fac];
}

The output looks like this in the player characters save file:

Code: [Select]
Factions ([ "Orcs of the Bloody Moon" : (["reputation" : 1, "level_timer" :
            729384181, "faction level" : 1, "reputation_timer" :
            729384219 ]) ])

Every 30 mudtime days I want to check the rtime variable ("reputation_timer") and decrement the rlev variable ("reputation") by 1 as follows:

Code: [Select]
void CheckTimer(){
    string *str = keys(Factions);
    int x, y;
    y = HOUR_LENGTH * DAY_LENGTH * 30;

    if(inherits(LIB_NPC, this_object())) return;
    if(!str) return;
   
    for(x=0; x<sizeof(str); x++){
        if (SEASONS_D->GetTime() >= (this_player()->GetReputationTimer(str[x])+y))
            this_player()->AddReputation(str[x], -1);
}

The player can/will be associated with many different factions as they progress in the game, so I need to check the reputation timers of the respective factions frequently.

What didn't work:  putting a heart_beat funtion in the factions.c file by replacing  void CheckTimer() in the above with static void heart_beat(). With static void heart_beat() combat got broke. (Disclaimer - I was warned not use the heart_beat() function outside of what was specified in other files inherited by the player character files by Quixadhal on dchat - well now I know why :) )

What SEEMS to work: keeping the above CheckTimer() function and adding the following to the player.c file in its static void heart_beat() function (after having player.c inherit the factions.c file)

Code: [Select]
factions::CheckTimer();

Is there a better/more efficient way to do this? Right now with the above code the mud seems to work fine, but I don't know if I'm wasting processor resources with the code.

Also, as an aside, in player.c there is this scope resolution call in static void heart_beat()
Code: [Select]
living::heart_beat();

but there's no heart_beat function in living.c! How does that work?  ???

7
General / Armor protection and weapon damage ranges
« on: January 12, 2015, 11:57:58 AM »
Just out of curiosity, I'm wondering what protection values others assign to armor and against what typical damage types.

What is your rationale behind having an armor protect against multiple damage types with different values assigned to their types?
Is there a specific range that you use for common and typical armors used in your scenario (instructions for builders)?
When a player has access to a description of the armor (let's say using an identify spell) what kind of information does the player get?
For example, an output might be something like:

This armor provides protection against the following damage types:
Blade - extraordinary
Blunt - poor
Shock - very good
etc.

To what values would the descriptions correspond?

Additionally, what are typical damage ranges used with the SetClass() function for weapons in the scenario?
Are there upper and lower limits that are restricted?

I know this is situational for each mud, and I've seen creator documents for other mudlibs, but nothing specifically for an up and running Dead Souls mud. Just looking for some input, or even better, if you have creator documents that can be shared for comparison purposes, that would be great!

8
Bug Central / Possible bug in meal.c
« on: December 13, 2014, 09:18:17 AM »
In meal.c there is this variable:
Code: [Select]
function MealAction;
defined in:

Code: [Select]
int SetMealAction(function f){
    MealAction = f;
}

The above function is not referenced in the /docs folder that I could find. No biggie. But, the function is only evaluated in 'mixed eventEat(object who)' in meal.c and not in 'mixed eventDrink(object who)'. Therefore, only meals that are eaten and not drunk get the benefit of the call to function if

Code: [Select]
SetMealAction((: foo :));
is set in a meal that is to be drunk defined by

Code: [Select]
SetMealType(MEAL_DRINK);

The following code must be added to the 'mixed eventDrink(object who)' portion of code in meal.c in order for drink meal types to make use of SetMealAction()

Code: [Select]
if( (x = functionp(MealAction)) && !(x & FP_OWNER_DESTED) ){
        evaluate(MealAction, who);
    }

The only other option discussed for making calls to a function was to use SetMealMessages() which has a check for a function pointer in the first value used in this function. SetMealAction() is useful for strictly added a call to your particular function added to the meal.

The below code is a an example for using SetMealAction() in a drink that has a one shot deal for curing paralysis in the player.
Code: [Select]
#include <lib.h>
#include <meal_types.h>

inherit LIB_MEAL;

void remove_paralysis();

static void create() {
    meal::create();
    SetKeyName("purewater");
    SetId( ({ "water","pure water" }) );
    SetShort("a vial of crystal clear pure water");
    SetLong("It is glowing!");
    SetMass(1);
    SetMealType(MEAL_DRINK);
    SetStrength(5);
    SetMealAction((: remove_paralysis :));
    //SetMealMessages((: remove_paralysis , "drink":));
    SetBaseCost("gold",500);
    SetNoCondition(1);
}

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

void remove_paralysis(){
    object ob = this_player();
    if(ob && ob->GetParalyzed()) ob->SetParalyzed(0);   
    tell_player(ob, "\nThe call to function worked!\n");
   
}

9
Bug Central / Possible bug in bonus object
« on: December 12, 2014, 07:21:11 PM »
In my experience the bonuses conferred by potions were being added to the player character but not removed after the "Duration" timer had timed out as specified in the /lib/bonus.c file. The bonus object was not being destructed as it should have been.

I believe the following piece of code starting at line 30 is the culprit in bonus.c:

Code: [Select]
void heart_beat(){
    if(Duration) Duration--;
    else eventDestruct();
}

When changed to the following:

Code: [Select]
void heart_beat(){
    if(Duration > 0 ) Duration--;
    else this_object()->eventDestruct();
}

the bonus object was destructed and the bonuses to the player were removed.

10
Bug Central / Solving ticktock headaches
« on: November 25, 2014, 01:23:02 PM »
Default Dead Souls hours in a day is 20 as defined in /lib/include/config.h

The ticktock command is set up for reporting as if the day length is 24 hrs with noon and midnight occurring at 12 pm and 12 am as exemplified by the following code starting at line 32:

Code: [Select]
if(hours >= 12  && hours != 24) {
        if(hours != 12) hours -= 12;
        meridiem = " pm";
    }

    if(!hours || hours == 0) hours = 12;

Change all the 12's to 10's since 10 is mid day and midnight.
Depending on your mud day length you would have to change the 12's to the mid day numbers (for example a 30 hour day mud would be 15).
The weirdness is that mid day is 10am and the time jumps to 1pm. But is that any weirder than going from 12 to 1?

So, change the default day time in config.h or change ticktock and be aware of the mid day times when using the ticktock command.

11
General / So I decide to jump on the GitHub bandwagon
« on: November 03, 2014, 04:14:03 PM »
Uploaded my DikuMud to Dead Souls areas port project at GitHub:

https://github.com/LashMUD/DikuMud-to-Dead-Souls-Port

Suggestions welcome.

12
Dead Souls Support / Bonuses and output of skills.c
« on: November 19, 2011, 03:53:54 PM »
Not really a bug per se, but I was using AddStatBonus() and AddSkillBonus() in a file I was creating - while the stat bonus was seen in the output for the players' stats command the skill bonus was not when using the players' skills command.

The skills command reported the skill level regardless of bonus points. I changed a line in skills.c so that the current skill level will be displayed.
This was bugging the crap out of me because I could see the bonus was being added using call <player>->GetSkillLevel() but the change wasn't being displayed when using the skills command.

Anyway, a minor change but may be useful to other newbies like myself.

diff file:

Code: [Select]
23c23
<             (/*mp["level"]*/this_player()->GetSkillLevel(skill) + "/" + max), x); //changed by Lash so current skill level is shown with skill bonuses
---
>             (mp["level"] + "/" + max), x);

13
Dead Souls Support / Adding SetActionsMap to npc.c
« on: October 15, 2011, 12:21:22 PM »
For having an npc that can perform more than one action each at different chances per heartbeat. Having more than one SetAction() function in an NPC or SENTIENT doesn't seem to work or, of course, I'm missing something. Nothing really special here - more or less copying and pasting code from room.c.

Modified file: ds3.6/lib/lib/npc.c

At line 45 added function prototypes:

Code: [Select]
private mapping ActionsMap = ([]);
private int tick_resolution = 5;

Inserted at line 119 in static void init()

Code: [Select]
if((Action && (sizeof(Action) || functionp(Action)))
            || sizeof(ActionsMap)){
        set_heart_beat(tick_resolution);
    }

Inserted at line 139 in static void heartbeat()

Code: [Select]
if( !GetInCombat() && sizeof(ActionsMap)){
        foreach(mixed key, mixed val in ActionsMap){
            if( val > random(100) ){
                if(functionp(key)) evaluate(key);
                else eventPrint(key);
            }
        }
    }

Inserted at line 632 in /lib/npc.c data functions section

Code: [Select]
mapping SetActionsMap(mapping ActMap){
    if(ActMap && sizeof(ActMap)) ActionsMap = ActMap;
    return copy(ActionsMap);
}

mapping GetActionsMap(){
    return copy(ActionsMap);
}

Example npc - a thief that has a chance to steal and pick up stuff as it goes along. I varied the values in SetActionsMap and the functions were called as expected for the SetActionsMap() value. Included is the function CheckNpc() to make sure this guy doesn't bash on other loaded npc's.

Code: [Select]
// Based on Diku MUD Alfa.  Program and Concept created by
// Sebastian Hammer, Michael Seifert, Hans Henrik Staerfeldt,
// Tom Madsen, and Katja Nyboe.
// http://www.dikumud.com
//
// Modified by Lash (Christopher Coker) for use with:
//
// The Dead Souls Mud Library version 2
// developed by Cratylus
// http://www.dead-souls.net

#include <lib.h>

inherit LIB_SENTIENT;

void CheckNPC();
void NpcSteal(object ob);
void Scavenge();

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

    SetKeyName("the thief");
    SetId( ({"thief"}) );
    SetAdjectives(({"non-player", "evil", "tricky"}));
    SetShort("A thief, all dressed in black");
    SetLong("Well, COUNT your money..!");
    SetRace("human");
    SetClass("thief");
    SetLevel(8);
    SetMelee(1);
    SetCanBite(0);
    SetGender("male");
    SetMorality(250); //?
    AddCurrency("gold", 100);
    SetActionsMap( ([
                     ( :Scavenge: ) : 10,
                     ( :NpcSteal: ) : 5,
                   ]) );
    SetWander(5);
    SetInventory( ([
         "/domains/diku-alfa/room/41.zon/meals/4103_slime" :1,
         "/domains/diku-alfa/room/41.zon/meals/4104_slime_poison" :1,
       ]) );
}

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

/*Do not attack other NPC's*/
void CheckNPC(object ob){
 
    object env=environment(this_object());
    if(ob && !inherits(LIB_NPC, ob)){
        eventForce("kill " +ob->GetKeyName());
 }
}

void NpcSteal(object ob){

    object env = environment(this_object());
    object *potvictims;
    int n1, n2, gold, level;
    level = this_object()->GetLevel();

    potvictims = filter(get_livings(env), ( :living($1) && $1 != this_object() && playerp($1):) );
   
    foreach(object target in potvictims){
        if(playerp(target)){
            n1 = random(level);
            if(n1 ==0){
                tell_object(target, "You notice " + capitalize(this_object()->GetKeyName()) + " trying to steal from you!");
                break;
            }
       
            if(n1 > 0){
                n2 = random(level)+1;
                gold = target->GetCurrency("gold") * n2 /100;
                this_object()->AddCurrency("gold", gold);
                target->AddCurrency("gold", -gold);
            }
        }
    }
}

void Scavenge(){

    object env = environment(this_object());
    object *item, *cost;
    int s;
   
    item = filter(all_inventory(env), (: !living($1) && (inherits(LIB_ITEM, $1) || inherits(LIB_ARMOR, $1)):) );
    cost = sort_array(item->GetBaseCost(), -1);
    s = sizeof(cost);
    if(s>0){
        foreach(object thing in item){
            if(thing->GetBaseCost() == cost[0]){
                eventForce("get "+thing->GetKeyName());
                break;
            }
        }
    }
}

14
I was trying to implement an armor item that enabled a bonus of +2 to "melee attack" using SetSkills with the LIB_BONUS feature.

When the item was equipped the bonus applied. However, when quickly equipping and unequipping the item the bonus started stacking. Initially melee attack was set at a value of 1 then after wearing the item melee attack was set to 3 as expected. If I removed and re-wore the item quickly the bonus started stacking - so 1 to 3, 3 to5, 5 to 7 , etc.

Interestingly, if I waited a few seconds after observing this stacking phenomenon then removed and rewore the item the skill and bonus went back to baseline. It seems that the mud needs some time to reset the bonus after unequipping the item. Here's the relevant code after SetWear( (:check_morality:) ) in the armor item file

Code: [Select]

varargs int check_morality(object who, string where){
    int x, y, morality;
    object env = environment(who);

    morality = who->GetMorality();
   
    x=who->GetSkillLevel("melee attack");
    who->eventPrint("Melee attack before skill bonus = "+x+".");
             
    //only less than 'good' chars can use

    if( morality <=200 ){
    object ob=new(LIB_BONUS);
    ob->SetSkills( (["melee attack" : 2]) );
    ob->eventMove(who);
   
    x=who->GetSkillLevel("melee attack");
    who->eventPrint("Melee attack after skill bonus = "+x+".");
   
    who->eventPrint("You can feel an increase in your fighting "+
           "prowess as you put on the gloves.");
        if(env) tell_room(env, who->GetName()+" wears "+
          GetShort()+".", ({who})); 
     
    return 1; 

    }

    else {
       who->eventPrint("You cannot seem to get your hands in "+
            "the gloves.");
       
        x=who->GetSkillLevel("melee attack");
        who->eventPrint("Melee attack not wearing gloves = "+x+"."); 
    return 0;

    }
       
}


Here's the output I received - wearing and removing is in quick succesion:

(Morality is too high)

wear gloves
Melee attack before skill bonus = 1.
You cannot seem to get your hands in the gloves.
Melee attack not wearing gloves = 1.

(Reset morality)

call me->SetMorality(200)
OBJ(lash /secure/save/creators/l/lash) -> SetMorality( 200 ) = 200
wear gloves
Melee attack before skill bonus = 1.
Melee attack after skill bonus = 3.
You can feel an increase in your fighting prowess as you put on the gloves.
remove gloves
You remove a pair of swordsman gloves.

wear gloves
Melee attack before skill bonus = 3.
Melee attack after skill bonus = 5.
You can feel an increase in your fighting prowess as you put on the gloves.
remove gloves
You remove a pair of swordsman gloves.

wear gloves
Melee attack before skill bonus = 5.
Melee attack after skill bonus = 7.
You can feel an increase in your fighting prowess as you put on the gloves.
remove gloves
You remove a pair of swordsman gloves.

(After waiting about 5 seconds after removing the gloves)

wear gloves
Melee attack before skill bonus = 1.
Melee attack after skill bonus = 3.
You can feel an increase in your fighting prowess as you put on the gloves.

I was wondering if having a check for "if not worn" every heartbeat to do something like:

Code: [Select]

int check_gloves(string skill) {
    write("Check gloves called");
    if( !GetWorn() ) {
        previous_object()->RemoveSkillBonus("melee attack", this_object());
        return 0;
    }


I had the above function called before checking for the morality condition but it did not make a difference in the stacking behaviour.




15
Bug Central / Default MudTime in Dead Souls
« on: January 05, 2009, 02:12:22 PM »
I was messing around with creating a set time for an npc to have an action and came across some weirdness.

The library DS2.8.4 is set with a default hours per day of 20 in config.h under lib/secure/include (#define DAY_LENGTH) .

When the mudtime goes from 19 hours and 59 minutes (9:59 pm mudtime) to 0 hours 0 minutes the mudtime comes back as 10:00 am and thus there are two 10 am's .

ticktock with no argument comes back as 12:00 am at this time.

It looks like what is happening is that mudtime is reporting time based on the number of hours and subtracting 10 based on a 20 hour day after the halfway point (as defined in mudtime.c as DAY_LENGTH/2 for determining "am" and "pm" under lib/secure/cmds/creators ).  When time goes into double digits mudtime gets screwey. The mud day looks like this:

Hour      Mudtime         Ticktock
0           10am               12am
1 -10     1am - 10am     1am-10am
11-19    1pm - 9 pm      11am-7pm

Weirdly, setting the hours per day in config.h to 24 gives the "expected normal" output. Mudtime and ticktock give the same time based on a 24 hour day. I just wanted to report on these findings in case someone else might find this useful. The relationship between mudtime and hours/day has to be played with to get a consistent output if left at the default setting of 20. I haven't found that code yet but will look into this more.

I guess this isn't a problem since mudtime is a creator command and a set time function based on hours in the mud will execute. It just got confusing waiting for my npc to do something at 11:00 am when "11:00 am" turned out to be 1:00pm instead :P

Here's the code I used for the npc I was building:

set_heart_beat(1);

heart beat function:

void heart_beat(){

    ::heart_beat();

    wander();

}

wander function:

int wander(){
int hour, minutes;       

    int *time_of_day;



    time_of_day = SEASONS_D->GetMudTime();

    hour = time_of_day[0];

    minutes = time_of_day[1];

    eventForce("say hour "+hour+"; minutes "+minutes+".");

other stuff

}

Lash

Pages: [1] 2