Creating a function to modify "opposite" stats based on a simple line in script.

I have created a system of Stats which are used to dramatically alter my game when incorporated with flags, descriptions, and scripts based on growing and changing player perspective. Using Pixie's CombatLib, I've modified some of the library to help keep the process simpler. If x flag and object has xdesc for printing descriptions, and the flags can be set or unset depending upon levels of stats and/or choices made in the game.

The stats are set up as "opposites", so they don't have negatives, but when one reaches zero, the next step down is actually an increase in the opposite stat:

Opposite stats:

agility ~ clumsiness
bravery ~ anxiety
charisma ~ antipathy
curiosity ~ disregard
intelligence ~ stupidity
selfawareness ~ selfdelusion
stamina ~ unhealth
stealth ~ overtness
strength ~ weakness
trust ~ cynicism
selflessness ~ greed
purity ~ perversion

Perspective flags are set based on choices in Character Generation and also final selection of one out of many "key" objects that allow access to the realms in the game.

Perspective flags:
Mage
Warrior
Diplomat
Collector
Ninja
Rich
Biblesight
Explorer
Traveler
Goth
Catlover
Gender
Bornagain
Pervert
Night
selfaware
alive
dead
possibly "Necromancer?"

Anyway, I made a function script that doesn't error, but I also haven't gotten it to do what I want when running script on a taken object.

Function:

A sample object I made to test the script is:

<object name="stattester">
  <inherit name="editor_object" />
  <take />
  <ontake type="script">
    statchg (player.charisma, 2, player.antipathy)
    statchg (player.curiosity, 2, player.disregard)
    statchg (player.stealth, 2, player.overtness)
    statchg (player.agility, 2, player.clumsiness)
    statchg (player.trust, 2, player.cynicism)
  </ontake>
</object>

I'm not sure what's missing from the code(s) for this to work. The list of stats includes all of these already have integers of different values to test their changes ("opposites" also have values)

Yet when I take the object, the player stats don't change at all.

Please and thanks for any info/ideas!


Sorry, don't know why the function didn't paste over:

function name="statchg" parameters="attribute, int, opattribute"><![CDATA[
if (attribute = 0) {
if (opattribute = 0) {
attribute = attribute +int
}
else if (opattribute = int) {
opattribute = 0
}
else if (opattribute > int) {
opattribute = opattribute -int
}
else if (opattribute < int) {
opattribute = 0
attribute = attribute +(int - opattribute)
}
}
else if (attribute > 0) {
attribute = attribute +int
}
UpdateStatus

trying without the closing line in case the browser/gui doesn't like complete functions. and now trying without opening less-than


Function parameters are passed by value.

When you call:

statchg (player.charisma, 2, player.antipathy)

Quest looks up the current value of player.charisma, the number 2 , and the current value of player.antipathy. Those 3 numbers are passed into the function.

The function then creates 3 new variables named attribute, int, and opattribute, whose initial values are the 3 numbers it was passed. Those variables are local: they only exist within the function, and are discarded when it finishes. The function does not know which attribute those values came from.

If you want the function to edit the original attribute, you need to pass in the name of the attribute.


trying without the closing line in case the browser/gui doesn't like complete functions. and now trying without opening less-than

You need to put a line of three backticks (```) above and below the function. You did this for one function in your first post, but not the other.

Yet when I take the object, the player stats don't change at all.

I think what you mean is something like:

<function name="statchg" parameters="attrname, i, oppname"><![CDATA[
  attrval = GetInt (player, attrname)
  oppval = GetInt (player, oppname)
  if (attrval = 0) {
    if (oppval = 0) {
      attrval = attrval + i
    }
    else if (oppval = i) {
      oppval = 0
    }
    else if (oppval > i) {
      oppval = oppval - i
    }
    else if (oppval < i) {
      oppval = 0
      attrval = attrval + (i - oppval)
    }
  }
  else if (attrval > 0) {
    attrval = attrval + i
  }
  set (player, attrname, attrval)
  set (player, oppname, oppval)
  UpdateStatus
</function>

For this logic, my first thought was that you were trying to make it so that if the stat is negative, it goes into the "opposite" stat instead, in which case it would be easier to work using a temporary variable which is allowed to be negative. Like this:

<function name="statchg" parameters="attr, i, opposite"><![CDATA[
  value = GetInt (player, attr) - GetInt (player, opposite) + i
  if (value > 0) {
    set (player, attr, value)
    set (player, opposite, 0)
  }
  else {
    set (player, attr, 0)
    set (player, opposite, -value)
  }
  UpdateStatus
</function>

Was that your intention?

Both of these would be called using the name of the attributes to change. For example:

statchg ("charisma", 2, "antipathy")

Mr. Angel!

Thank you for the clarity! The "working with functions" tutorials that look like they'd get more specific are the ones that seem to be missing or links redirected to general menus.

You are astute. Yes, rather than having positive and negative greed values, and merely adding or subtracting integers from my stats, I want it set up so that bringing a stat "past" zero via subtracting actually keeps the stat at zero and adds remaining points to the "opposite" stat. And then also checking that When I add value to a stat that's at zero, the "opposite" stat is subtracted from down to zero first before the remaining value is actually added to the target stat... I can't remember off-hand, but I think I still had planned to have to work that bit of math in, or thought it might have to go into a separate function.

I'll take the knowledge you just gave me and work with it (if it's not actually already the perfect solution) to see what I can come up with. I hadn't seen that the function is only taking the values of the attributes and not actually "recognizing" which attributes they are and then applying the new values at the end. Your code looks (at first glance to a novice) as if you both understood what I was doing and made it work, and understood how functions work better than I did.

Thank you again! And thanks for being here for folks in the forum and for your super prompt response!


Ok, finally had a mo to try these codes out, and it looks like both failed for essentially the same reason, but I'm not precisely sure on what that reason is. Is it that the function still isn't reading the input from taking the "stat tester" object above?

statchg (player.charisma, 2, player.antipathy)

As an example of one of the five sets of attributes, run through your first suggestion errors as:

Error running script: Error compiling expression 'GetInt (player, attrname)': FunctionCallElement: Could find not function 'GetInt(Element, Int32)'
Error running script: Error compiling expression 'GetInt (player, attrname)': FunctionCallElement: Could find not function 'GetInt(Element, Int32)'
Error running script: Error compiling expression 'GetInt (player, attrname)': FunctionCallElement: Could find not function 'GetInt(Element, Int32)'
Error running script: Error compiling expression 'GetInt (player, attrname)': FunctionCallElement: Could find not function 'GetInt(Element, Int32)'
Error running script: Error compiling expression 'GetInt (player, attrname)': FunctionCallElement: Could find not function 'GetInt(Element, Int32)'

and through the second script errors as:
Error running script: Error compiling expression 'GetInt (player, attr) - GetInt (player, opposite) + i': Unknown object or variable 'attr'
Error running script: Error compiling expression 'GetInt (player, attr) - GetInt (player, opposite) + i': Unknown object or variable 'attr'
Error running script: Error compiling expression 'GetInt (player, attr) - GetInt (player, opposite) + i': Unknown object or variable 'attr'
Error running script: Error compiling expression 'GetInt (player, attr) - GetInt (player, opposite) + i': Unknown object or variable 'attr'
Error running script: Error compiling expression 'GetInt (player, attr) - GetInt (player, opposite) + i': Unknown object or variable 'attr'

Trying with just the attribute names, but not player:

statchg (charisma, 2, antipathy)
statchg (curiosity, 2, disregard)
statchg (stealth, 2, overtness)
statchg (agility, 2, clumsiness)
statchg (trust, 2, cynicism)

in both the first and second function throws:

Error running script: Error compiling expression 'charisma': Unknown object or variable 'charisma'

Which would make me think that the "player." needs to be added back in so it knows the "object" where the attribute is found... but we already saw the error that leads to.

Experimenting a bit, but I'll thank you for any help!


Tried changing the input to this with or without space between "GetName" and the parenthesis and quest wouldn't read either to create the script for passing into the function.

statchg (GetName(player.charisma), 2, (GetName(player.antipathy))
statchg (GetName(player.curiosity), 2, GetName(player.disregard))
statchg (GetName(player.stealth), 2, GetName(player.overtness))
statchg (GetName(player.agility), 2, GetName(player.clumsiness))
statchg (GetName(player.trust), 2, GetName(player.cynicism))


I'm trying to figure out the way you mentioned of "passing the attribute name" instead of the value through the function. I had imagined that your scripts handled that.

I could figure out a complicated script with a bunch of ifs, and just substitute the attributes each time depending on which ones I wanted to modify... but the whole goal was to put the heavy work into the function and have something simpler to put in the scripts on the objects.

Before I got the idea of the "opposites" I was putting in each circumstance something like
player.charisma = player.charisma +1
player.greed = player.greed = +2
player.stealth = player.stealth -2

but then I realized I could have all attributes that kept positive values by completing pairs of opposites, and hopefully also create a function that would keep things pretty simple on the triggering end.


statchg (player.charisma, 2, player.antipathy)

If you do this, you are passing the value of the attribute. The function expects the name of the attribute, so this doesn't work.

statchg (charisma, 2, antipathy)

In this case, you are trying to pass the value of local variables called charisma and antipathy. You get an error because those variables don't exist.

statchg (GetName(player.charisma), 2, (GetName(player.antipathy))

That's a good thought; but there is no function GetName that does this. It is not possible to look at a number and work out which attribute it came from. You need to just give the function the name, and let it look up the value.

Which would make me think that the "player." needs to be added back in so it knows the "object" where the attribute is found

The function I gave already knows that the string it receives is the name of an attribute of the player object. It uses the expression GetInt (player, attr) to get the value of an integer attribute of the player object whose name is in the string variable attr.

I'm trying to figure out the way you mentioned of "passing the attribute name" instead of the value through the function. I had imagined that your scripts handled that.

The example I gave was:
statchg ("charisma", 2, "antipathy")

Note that there are quotes (") around the word charisma, telling Quest to treat it as text, rather than the name of a variable or function.

This is what the function I gave you expects: the name of the attribute, rather than the value of it.

but then I realized I could have all attributes that kept positive values by completing pairs of opposites, and hopefully also create a function that would keep things pretty simple on the triggering end.

Using a feature called "changescripts", you could make it so that code like player.charisma = player.charisma - 3 automatically calls a function in the background. But I would say that is a fairly advanced topic (and it's quite easy to crash the game with an error in a changescript, which makes them hard to debug). So probably better to stick to functions for now.


Thank you again Mr. Angel! Ok, so I was missing the Quotation marks. I'm starting to learn about how to use the functions from you!
Maybe you should make a little tutorial or documentation on these things for us?

So quotes as input into the function tells the function that we're sending text, not interpreting the value of something! I'm definitely saving that important bit in my notes!

Thank you again, going to try it with the quotes now!


It works perfectly as imagined, thank you Mr. Angel for teaching me about functions!

The only slight change I had to make was because a little bit of the original math was foolish human math.

else if (oppval < i) {
  oppval = 0
  attrval = attrval + (i - oppval)

That doesn't work, because I zeroed the oppval before adding to the attrval! That's how we'd normally do it in our minds "first we add the stuff to the negative and make it zero, then put the remainder onto the positive value. but the function obviously added zero to the equation "i-oppval". The current Oppval after zeroing, not the original Oppval held in a short-term memory somewhere!

Fixing it was as simple as moving the last bit before the bit above it!

Thanks again! You're an awesome resource Mr. Angel!


smh. I made the new function "statchgup" because it worked for adding positives to the system and began work on a "statchgdn" for subtracting (adding negatives) to the system. Got it to about half-work, then remembered "Wait, Mr. Angel made a second version of the function to try too!"

Your second version works perfectly and required no revision. Not to mention being shorter and cleaner.

You're a coding genius, Mr. Angel.


smh. I made the new function "statchgup" because it worked for adding positives to the system and began work on a "statchgdn" for subtracting (adding negatives) to the system.

I was kind of wondering whether you were only intending to use it one way; or whether that was an oversight.

Although… I don't think a second function would actually be necessary. With the version I wrote, these two have the same effect:

  • statchg ("strength", -2, "weakness")
  • statchg ("weakness", 2, "strength")

Interesting different way to look at the stats.

(This reminds me of a tabletop RPG system I used many years ago, which also had negative stats… but they didn't act as negatives in that one. Somebody with strength 1/weakness 0 would be pretty average and unremarkable, while someone with strength 8/weakness 8 would never fail to open a heavy door, but would have a very high chance of suffering an injury in the process)


Very interesting! idea with the "opposite" stats Mr. Angel. It sounds like it would be a fun play.

You know, I didn't stop to think about the negation being commutative like that in the formula! Makes perfect sense. Sometimes I get a little hung up in the brain with negation.

I like your second formula much better though. Very clean and neat.


As I enter the changes in stats using the new, I'm now wondering if there's an even more complex function I can make that could allow the input to be as simple as:

statchg ("perversion", +2)

Is there a way to allow a function to look up a dictionary or something that has the opposite stats all predefined? That way it would know to do all that math and checking with the other stat even with the input from the script giving just the one stat?

I guess I'm now looking for a "functions in Quest" tutor haha.


Is there a way to allow a function to look up a dictionary or something that has the opposite stats all predefined?

I was thinking about that.
One way would be to have a stringdictionary attribute; game.stat_opposites or something, where the key is a stat and the value is its opposite. Then you could just change the function as:

<function name="statchg" parameters="attrname, i"><![CDATA[
  oppname = DictionaryItem (game.stat_opposites, attrname)
  attrval = GetInt (player, attrname)

and then continue as before.


Simple and yet awesomely affective.
So, if I understand correctly, for the function to work well, I'd have to have something like:

Key: selflessness value: greed
Key: greed value: selflessness

Etc.

Where each individual stat is defined by its opposite. I'm going to attempt to make and implement it now!


so modified for the second function you gave, I have:

opposite = DictionaryItem (game.stat_opposites, attrname)
value = GetInt (player, attr) - GetInt (player, opposite) + i
if (value > 0) {
set (player, attr, value)
set (player, opposite, 0)
}
else {
set (player, attr, 0)
set (player, opposite, -value)
}
UpdateStatus


works beautifully, but I had to change it ever so slightly more than just above.

I decided to put the string dictionary on the player, because I haven't been using game.pov for these. I'm thinking if I do have changes of pov, I'll also have different stats and different perspective flags and descripton scripts for those.

So I put the dictionary on the player and also had to change attrname to attr because I'm using your second suggested function.

opposite = DictionaryItem (player.stat_opposites, attr)
value = GetInt (player, attr) - GetInt (player, opposite) + i
if (value > 0) {
set (player, attr, value)
set (player, opposite, 0)
}
else {
set (player, attr, 0)
set (player, opposite, -value)
}
UpdateStatus

a tidy solution which makes it easy to modify this complex stat system with simple script.

As always, thanks again mrangel


Kinda late here, and didn't read most of the comments... but
Why have 2 attributes: agility ~ clumsiness
when you can just use agility...
but adjust the values from positive to negative...
IE: if agility is +5, then you are agile, but...
if agility is -5, then you are clumsy...
This sounds much more simple to me...
(I'm checking back through the comments now...)


Here's an alternate version; without the function.

You could give the player a script attribute named changedstrength with code like:

if (not this.strength = 0) {
  value = this.strength
  if (not this.weakness = 0) {
    value = value - this.weakness
  }
  if (value > 0) {
    this.weakness = 0
    this strength = value
  }
  else {
    this.strength = 0
    this.weakness = -value
  }
}

(off the top of my head… I might have made some careless error there)

If you do this for all the attributes (including the negative ones) then you can just change values with something like:

player.strength = player.strength - 3

and it will automatically redirect the points into weakness if necessary.


@DarkLizerd

when you can just use agility...
but adjust the values from positive to negative...

I assume it's a look-and-feel thing. Mathematically it's equivalent; but it can give different player experience. Like how a stat from 1-100 feels different from a stat that goes 0-10 with one decimal place, even though they're the same numbers behind the scenes.


Yes @Darklizerd! That's the idea. I want the "Ninja," the "Rich," and maybe the Necromancer characters to have "Greed" and "Cynicism" rather than "negative Selflessness" and "negative trust" stats. And the "Born again" to be dealing with "Selflessness" and "Purity" rather than "negative Greed" and "negative" perversion, etc. This game is going to have a myriad of perspectives and biases affecting not only descriptions, but which objects in the game are accessible, etc.

The stats are one of the ways to track and change these mid-game. Also going to use flag system. I already have a "perspectives" function to work with descriptions, and am thinking about a "change alias" function.

There will also be objects/exits, etc hidden and/or inaccessible depending upon stats and flags.

So depending upon one's perspective they might see the "Bible" as "The Holy Bible" or "a book of hate speech" or "Abraham Lincoln's Bible" or "an old Bible." It might be the one artifact out of the group which makes extra special things happen for them, or maybe they can't see and interact with it at all, etc.

Each character "type" will have it's own artifact which is powerful for everyone who can get it, but more powerful for them.

There are also doors in the world which lead to dramatically different worlds/realms. Each accessible after getting these key artifacts which are more suited to one "type" of character over all the others. So each type has their own "world" which is going to be more at home for them, and other worlds they might be able to discover how to enter, but which are more or less suited to them in different ways.

I'm having a lot of fun with the concept development, and also fun with discovering how to work the code. Super indebted to Pixie and Mrangel for their contributions in forum and via tutorials and libraries.


Thanks for the additional ideas Mrangel!

If I don't use it for this precisely, it certainly may come in useful elsewhere.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums