Does Appraise work?

I was testing some things with stores, I had the store just set with Buy pct 100 and sell pct 100, and an item with value 100gp.

PC had a 1 appraise, and store would pay 100 gp for the item.
Put a +5 appraise necklace on the PC and now store would pay 98 gp…

I tested 3 stores, 1 with Buy 100, Sell 100… 1 with Buy 50, Sell 100 and finally Buy 100, Sell 50.
The stores Sell pct seems to work correctly, but Buy price was inconsistent.

That sounds really odd. I wish I could help you, but I haven’t used or examined appraise much. Maybe the game rolls a dice when you have appraise and that’s why the price is inconsistent? I would guess someone like Lance Botelle would know about this, since he’s done so much tampering with the core game and its systems for his modules.

1 Like

i ended up rewriting N2_AppraiseOpenStore()

It doesn’t address any bug(s) that may be on a store’s blueprint. But it seems to work well enough for me …

The stock function is in the #include ‘ginc_item’ … the function in ‘nw_i0_plot’ gplotAppraiseOpenStore() is obsolete and probly shouldn’t be used anymore. It adjusts both pc and merchant skills by d10()

N2_AppraiseOpenStore() is called by ~10 scripts. The primary caller is the stock dialog script ‘ga_open_store’ – that at least should be copied to /override and recompiled with any changes,

// Prints results to chat when opening a store.
void PrintAppraiseResult(object oPc,
                         int iFavorPc,
                         int iAppraisePc,
                         int iAppraiseMerchant,
                         int iMarkup,
                         int iMarkdown)
{
    string sPrint = "_Open Store : ";

    if      (iFavorPc >  5) sPrint += "<c=springgreen>Merchant is favorable.</c>";
    else if (iFavorPc < -5) sPrint += "<c=firebrick>Merchant is not favorable.</c>";
    else                    sPrint += "<c=mediumslateblue>Merchant is neutral.</c>";

    sPrint += "\nAppraise skill "            + IntToString(iAppraisePc)
            + " vs Merchant Appraise skill " + IntToString(iAppraiseMerchant);

    if      (iMarkup > 0) sPrint += " . . . " + IntToString( iMarkup) + "% sales price increase";
    else if (iMarkup < 0) sPrint += " . . . " + IntToString(-iMarkup) + "% sales price decrease";

    if      (iMarkdown > 0) sPrint += " . . . " + IntToString( iMarkdown) + "% buying price increase";
    else if (iMarkdown < 0) sPrint += " . . . " + IntToString(-iMarkdown) + "% buying price decrease";

    SendMessageToPC(oPc, sPrint);
}


// Opens the Store taking Appraise skills into account.
// - an opposed skill comparison: PC's appraise skill vs. the shopkeeper's
//   appraise skill.
// - see also gplotAppraiseOpenStore() in 'nw_i0_plot'
// - does not consider MarkUp and MarkDown on the store's blueprint but only the
//   values passed by the script that opens the store
void N2_AppraiseOpenStore(object oStore, object oPc, int iMarkup = 0, int iMarkdown = 0)
{
    int iAppraisePc       = GetSkillRank(SKILL_APPRAISE, oPc);
    int iAppraiseMerchant = GetSkillRank(SKILL_APPRAISE, OBJECT_SELF); // - N2_GetNPCEasyMark(OBJECT_SELF); has issues

    if (iAppraiseMerchant < 0)
        iAppraiseMerchant = 0;

    int iStrref;

    int iFavorPc = iAppraisePc - iAppraiseMerchant; // NOTE: reversed from stock function.
    if (iFavorPc > 5)
    {
        iStrref = 182470; // "[Appraise Skill] Merchant's reaction is favorable."
        if (iFavorPc > 30)
            iFavorPc = 30;
    }
    else if (iFavorPc < -5)
    {
        iStrref = 182468; // "[Appraise Skill] Merchant's reaction is unfavorable."
        if (iFavorPc < -30)
            iFavorPc = -30;
    }
    else
        iStrref = 182469; // "[Appraise Skill] Merchant's reaction is neutral."

//  FloatingTextStrRefOnCreature(iStrref, oPc, FALSE);
    SpeakString(GetStringByStrRef(iStrref)); // OBJECT_SELF shall be merchant

    // 'iMarkup' is added to the store's price on items SOLD by the store (-100 to 100)
    // - negative is good for player
    // - effect on selling items is only half of effect on buying items.
    //   kL_note: which is effed since that ought be a decision of the designer(s)
    //            ie. passed in as an arg w/ default
    iMarkup -= iFavorPc / 2;
    // 'iMarkdown' is added to the store's price on items BOUGHT by the store (-100 to 100)
    // - positive is good for player
    iMarkdown += iFavorPc;

    PrintAppraiseResult(oPc,
                        iFavorPc,
                        iAppraisePc,
                        iAppraiseMerchant,
                        iMarkup,
                        iMarkdown);

    OpenStore(oStore, oPc, iMarkup, iMarkdown); // <- hardcoded
}

 
@Lance_Botelle

2 Likes

Hi all,

@THughes281
@andgalf
@kevL_s

My own campaign script works along the lines of how KevL describes. I have made my own edited version of the ga_open_store script (copy of the official one) in my campaign folder, which for anybody not setup that way would go into their override instead.

Then you can edit it to do those things you need when it opens the store, like markups and other stuff. The script below WILL NOT COMPILE, as it is just an extract from my own edited ga_open_store, which has many other aspects that have been altered for my own campaign usage … It also checks if an NPC has been charmed for “benefits”.

I show it just to give another view of how the code may be altered in this area. E.g. My own code checks for the best appraiser in the party to avoid the player having to keep switching PCs to get the best prices. They can simply get the best prices based on the highest appraise skill currently available in the group.

It has been a while since I checked this script, but I think these parts were working OK. The problem is that the script also uses many custom functions and so best to stick with KevLs script for ease of use.

    /////////////////////////////////////////////////////////////////////////////////////
    	// MAKE SURE THE BEST VALID CAPABLE PC IS DOING THE BARTERING
    	/////////////////////////////////////////////////////////////////////////////////////
    	
    	object oBarterer = CheckPCsForHighSkill(oMainPC, SKILL_APPRAISE, FALSE, TRUE, 15.0, 1);
    	
    	DelayCommand(1.0, SetNoticeTextAll("PC BARTERING THE STORE PRICES: " + GetStringUpperCase(GetName(oBarterer)), 1, 0, oPCStart));	
    		
    	// PCS APPRAISE SKILL
    	int nPlayerSkillRank = GetSkillRank(SKILL_APPRAISE, oBarterer);

        // NPC CURRENT APPRAISE STATE (SUBJECT TO ANY CHARMS)
    	int iBONUS = 0; if(GetLocalInt(OBJECT_SELF, "INFLUENCED")){iBONUS = 10;}
	
	int nNPCSkillRank = GetSkillRank(SKILL_APPRAISE, OBJECT_SELF) - iBONUS; 

        if (nNPCSkillRank < 1 ){nNPCSkillRank = 1;}
    		
    	// BROADER NEUTRAL ZONE
    	string sState = "[Appraise Skill]: Merchant's reaction is neutral.";
    	if (nNPCSkillRank + 5 < nPlayerSkillRank){sState = "[Appraise Skill]: Merchant's reaction is favorable.";}	
    	else if (nPlayerSkillRank < nNPCSkillRank - 5){sState = "[Appraise Skill]: Merchant's reaction is unfavorable.";}   	
    		
    	// DISPLAY RESULT
    	FloatingTextStringOnCreature(sState, oPCStart, FALSE);

        // * determines the level of price modification
    	int nAdjust = nNPCSkillRank - nPlayerSkillRank; 
    	// * Hard cap of 30% max up or down
        if (nAdjust > 30){nAdjust = 30;}
        if (nAdjust < -30){nAdjust = -30;}

    	// nBonusMarkUp is added to the stores default mark up percentage on items sold by PC (-100 to 100)
    	// (positive is good for player)
    	// Effect on selling items is only half of effect on buying items.
        
    	nMarkUp = nMarkUp + nAdjust/2;
    	
    	// nBonusMarkDown is added to the stores default mark down percentage on items bought by PC (-100 to 100)
    	// (negative is good for player)
    	
    	nMarkDown = nMarkDown - nAdjust;
        
    	DelayCommand(0.4, OpenStore(oSTORE, oPCStart, nMarkUp, nMarkDown));
1 Like

hey Lance,

note the note in N2_GetNPCEasyMark()

// Gets a value that will be subtracted from oTarget's DC to resist Appraise or
// Persuasion.
// kL_note: Test highest value first. Thanks.
// NOTE: Although this is intended to influence neutral or friendly merchants
// they cannot be targeted by any of these spells because the spells are hostile.
// - see also GetNPCEasyMark() in 'nw_i0_plot'
int N2_GetNPCEasyMark(object oTarget)
{
    if (   GetHasSpellEffect(SPELL_DOMINATE_PERSON,  oTarget)
        || GetHasSpellEffect(SPELL_DOMINATE_MONSTER, oTarget)
        || GetHasSpellEffect(SPELL_DOMINATE_ANIMAL,  oTarget))
    {
        return 20;
    }

    if (GetHasSpellEffect(SPELL_MASS_CHARM, oTarget))
        return 15;

    if (   GetHasSpellEffect(SPELL_CHARM_PERSON,           oTarget)
        || GetHasSpellEffect(SPELL_CHARM_MONSTER,          oTarget)
        || GetHasSpellEffect(SPELL_CHARM_PERSON_OR_ANIMAL, oTarget))
    {
        return 10;
    }

    return 0;
}

unless you’ve turned those spells into Master/Child spells that give player the option to target a friendly … not sure of the details, just mentioning it

( and doing so might not be compatible with the core AI, if done willy nilly )

@kevL_s,

Yes, I started to wonder the same thing about spell targetting … It was so long ago I first tampered with this, that I am considering taking a closer look at it altogether.

I “think” (but need to double-check) that I may have adapted the spell to allow such targetting, but now I am not so sure.

I will double check now and give feedback.

EDIT: I think this was once “covered” by the ability for players to be able to change a target hostile for just such things. However, I have altered things slightly since then and this is not always possible. Therefore, I may have to reconsider this for the future - although the function itself currently does nothing … at least it can stay in possible future usage.

Thanks, Lance.

1 Like

@kevL_s,

OK, after doing some careful testing, I think any “charms” require some careful application with “friendly” targets, mainly because any successful charming prevents any conversation from starting properly.; thereby making any effects pointless as they currently work.

However, I do believe altering the 2da to change the target type (via location), then the appropriate spells may well be able to be altered to apply a “benefit”. However, as the N2_GetNPCEasyMark function checks for the specific effect being present, then I would remove this and simply use a variable added at spell cast (and dropping out before applying the effect itself) so that the spell applies a potential benefit (or may make the store owner less friendly if the spell was noticed) that is used directly at the time of store opening (this script).

Unless I edit this post, I think that’s what I will do.

Thanks, Lance.

EDIT: I am now working on any charm type spells (as recognised by the normal N2_GetNPCEasyMark function, except excluding Dominate Animal) so that they will now apply a one minute “influence rating”, which will give a bonus of ten all the while it is in place. i.e. The bonus will not vary, neither will its duration. It will be the basic bonus of the store owner fails the save, and will last one minute for all who then use the merchant. Currently, the “influence” only affects those friendly who are of the merchants faction. (Script updated above to reflect new variable bonus for successfully charmed merchants.)

EXAMPLE CODE ADDED TO SPELL

// NEAREST GROUND TARGET
	if(oTarget == OBJECT_INVALID)
	{
		location lLoc = GetSpellTargetLocation();
		oTarget = GetNearestCreatureToLocation(CREATURE_TYPE_IS_ALIVE, CREATURE_ALIVE_TRUE, lLoc);			
	}	
//////////////////////////////////////////////////////////////////////////////////////////////
// ALLOW A MERCHANT INFLUENCE (BUT DO NOT APPLY THE EFFECT OR WILL PREVENT CONV)
// CHARMED STORE OWNERS ARE AFFECTED FOR A MINUTE ONLY
//////////////////////////////////////////////////////////////////////////////////////////////

int iALLOW = 0;		
object oMerchant = GetObjectByTag("SETFACTION_MERCHANT");
if(GetFactionEqual(oMerchant, oTarget)){iALLOW = 1;}

if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF) || iALLOW)
  	{

Later in the same …

//Make a Will save to negate
if (!MySavingThrow(SAVING_THROW_WILL, oTarget, GetSpellSaveDC(), SAVING_THROW_TYPE_MIND_SPELLS))
{
	if(iALLOW)
	{
		SetLocalInt(oTarget, "INFLUENCED", 10);										
	}
	
	else
	{
		eCharm = FIXEDGetScaledEffect( EffectCharmed(), oTarget );	// 11/09/06 - BDF(OEI): this line was missing, causing PCs to get charmed
		//Apply the linked effects and the VFX impact
		DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCharm, oTarget, RoundsToSeconds(nDuration)));
		DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
	}
}

The “INFLUENCED” var decreases by one each round within the merchants heartbeat.

The spells.2da references have had target bitwise to include ground target where required. E.g. Charm person from 0x2A to 0x2E.

1 Like

why not just

    int iBONUS = GetLocalInt(OBJECT_SELF, "INFLUENCED");

@kevL_s

Because I reduce the “INFLUENCED” variable by one point per round in the creatures heartbeat. It acts a duration counter (unique to merchants faction).

So if it gets reduced (and I was using it as the bonus ref), the bonus would drop over time, which is not necessarily a bad thing, but this way, it gives the player a minute to start the conversation that leads to opening the store. Also, I don’t decrease the variable when the NPC is speaking, so this var will only deplete after about a minute of being left alone.

During that time, the bonus remains at 10. i.e. Any “INFLUENCED” value will give the bonus of 10. :slight_smile:

Thanks for asking, Lance.

1 Like

@THughes281
I’m pretty sure that is by design. You should never be able to sell something to a merchant for a price higher than what the merchant will sell it for. Otherwise, you would be able to exploit the merchant.
e.g. You could buy a tower shield from a merchant for 100gp. Then sell the tower shield right back to him for 120gp. Then buy the same shield from the merchant again for 100gp and then turn around and sell it right back to him for another 120gp. Rinse and repeat for a money exploit.

1 Like

@travus,

However, if a player does have the skill to craft something, then I think it’s worth rewarding them with the gold (as payment) if they are prepared to take the time crafting it. After all, they have taken the skill, so why not let them gain the odd bit of gold as a reward … It’s not something that can really change the balance without a player spending more time crafting than adventuring … and we all know adventuring is the fastest way to earn loot. :wink: And, we also control what exactly can be bought anyway.

1 Like