Score System error

magano
Now I am creating a puzzle game called "The Aftermath" which is set in the future. Not much descriptions, but at least it's still passable

Now in that game, I have A LOT of PROBLEMS. Listing some minor ones:
1. The score system is WAY too long (using a lot of ifs, I don't have any more ideas on ho to simplify them)
2. A random Int32 error (during calculations as I used a lot of numbers)
3. Broken English ( :D )

But now I have a big problem in the score system
This was the code for the system: (SPOILER ALERT)
<function name="CountTotalScores" parameters="CurrentTurns, O2Remaining" type="string"><![CDATA[
multi = 0.8
subtotpts = 0 // Subtotal
totpts = 0 // Total
gcb = 0 // Game Completion Bonus
jewelbonus = 0
quarkbonus = 0
jewelvaultbonus = 0
radiobonus = 0
milestonebonus = 0
stagebonus = 0
ammoclipbonus = 0
gallardcent = 0
easteregg = 0
// 1st: Base Score
// 2nd: Jewel Bonus (from Dice Roll)
// 3rd: Quark Bonus
// 4th: Jewel Bonus (from Vault)
// 5th: Radio Bonus
// 6th: Milestone Bonus
// 7th: Stage Bonus
// 8th: Ammo and Clip remaining Bonus
// 9th: Finding of Gallard cent
// 10th: Easter Eggs
// 11th: Game Completion Bonus (Turns, O2 Remaining)
// 12th: All 11 multiplied by a multiplier
if (Got(jewel)) {
jewelbonus = GetRandomInt(3000,6000)
multi = multi + 0.08
}
if (Got(quark)) {
quarkbonus = GetRandomInt(1000,4000)
multi = multi + 0.05
}
if (Got(jewelvault)) {
jewelvaultbonus = GetRandomInt(6000,10000)
multi = multi + 0.11
}
if (Got(radio)) {
radiobonus = GetRandomInt(750,3000)
multi = multi + 0.01
}
if (GetBoolean(player, "mil1")) {
milestonebonus = milestonebonus + GetRandomInt(300,900)
}
if (GetBoolean(player, "mil2")) {
milestonebonus = milestonebonus + GetRandomInt(2000,4000)
multi = multi + 0.01
}
if (GetBoolean(player, "mil3")) {
milestonebonus = milestonebonus + GetRandomInt(700,1200)
}
if (GetBoolean(player, "mil4")) {
milestonebonus = milestonebonus + GetRandomInt(1300,2300)
multi = multi + 0.03
}
if (Got(gallardcent)) {
gallardcent = milestonebonus + GetRandomInt(12000,24000)
multi = multi + 0.1
}
stagebonus = GetRandomInt(10000,20000)
ammoclipbonus = smgun.clip * GetRandomInt(200, 400) + smgun.ammo * GetRandomInt(40,100)
if (GetBoolean(toilet, "easteregg")) {
easteregg = easteregg + GetRandomInt(700,1000)
multi = multi + 0.01
}
gcb = ((500 - CurrentTurns) * GetRandomInt(30,90)) + (O2Remaining * GetRandomInt(30,90))
subtotpts = jewelbonus + quarkbonus + jewelvaultbonus + radiobonus + milestonebonus + stagebonus + gallardcent + ammoclipbonus + easteregg + gcb + game.score
totpts = subtotpts * multi
objbonus = jewelbonus + quarkbonus + jewelvaultbonus + radiobonus + ammoclipbonus
strres = "<b>Score Calculation</b><br />Base Score: " + game.score + "<br />Object Bonus: " + objbonus + "<br />Milestone Bonus: " + milestonebonus + "<br />Stage Bonus: " + stagebonus + "<br />Gallard cent Bonus: " + gallardcent + "<br />Easter Egg Bonus: " + easteregg + "<br />Game Completion Bonus: " + gcb + "<br />Subtotal Score x Multiplier: " + subtotpts + " x " + multi + "<br />-------------------------------------<br />Total Score: " + totpts
return (strres)
]]></function>


When I ran the function, this error was thrown:
Error running script: Error evaluating expression 'ListContains(ScopeInventory(), obj)': Unable to cast object of type 'System.Int32' to type 'TextAdventures.Quest.Element'.

But I don't have the expression ListContains(ScopeInventory(), obj) at all!
What did I do wrong?

jaynabonne
"Got is implemented as:

return (ListContains(ScopeInventory(), obj))

so the problem is some usage of Got. And here it is:

if (Got(gallardcent)) {

where earlier in the function, you have:

    gallardcent = 0

"Got" is used for objects, so I'm not even quite sure what you're trying to do. Perhaps you meant to check for something else to determine the value for gallardcent?

magano
Oh yeah... I must have used the same name as both the object and the variable at the same time and that caused an unwanted clash
That's because gallardcent is also an object name, and i used it as a variable name, and things got messed up

jaynabonne
Ah, I see. Yes, a "scoping" problem. :)

magano
Oh, another problem. How could I assign 2 values to a single status attribute without doing that as a string? because I'll take that value in a function as an integer (both of the values) and It's too complex to change

jaynabonne
What you're looking for (I think) is some sort of composite data structure - a way to group data together into a single entity. There are three main ways in Quest to accomplish that: objects, lists, and dictionaries. I'll give examples of all three for this specific case.

(In the examples that follow, though I'm speaking of passing values to a function, the general usage of these things to hold aggregate data can be applied even outside of function calls.)

Using Objects to Pass Values

An object can be passed as a parameter to a function, to allow all of its attributes to be available as once. If you have an object named "Params", and a function that looks like this:

  <function name="ObjFunction" parameters="params">
msg("ObjFunction: arg1 = " + params.arg1)
msg("ObjFunction: arg2 = " + params.arg2)
</function>

then you can call the function (for example) like this:

      Params.arg1 = "parameter 1"
Params.arg2 = 314
ObjFunction(Params)


Note that this uses a pre-existing object. You can also create objects on the fly, but it's more complicated and probably not worth the effort, given that you can achieve more or less the same effect with dictionaries below.

Using Lists to Pass Values

A generic list in Quest (not a "string list", "object list", etc unless you want all values to be of that type) can be used to pass a set of values to a function, accessed by an index. If you have a function like this, which takes a list:

  <function name="ListFunction" parameters="params">
msg("ListFunction: arg1 = " + params[0])
msg("ListFunction: arg2 = " + params[1])
</function>

then you can call it with something like:

      player.plist = NewList()
list add (player.plist, "list parameter 1")
list add (player.plist, 628)
ListFunction(player.plist)

Note that you don't need to set it into an object attribute. I'm just doing that since you wished it to be so. You could use a local variable if desired. And you don't have to call the function in the same code block where you set up the attribute. (I hope that's clear.) You can set up the attribute in one place and then call the function using it from some other part of your code.

This works ok, but the indices are a bit opaque. What does "0" mean? How horrible is it to renumber everything if I need to get rid of one of the values later? The dictionary solves those two problems nicely.

Using Dictionaries to Pass Values

Finally, you can use dictionaries to create aggregate structures. Assuming a function like this:

  <function name="DictFunction" parameters="params">
msg("DictFunction: arg1 = " + params["arg1"])
msg("DictFunction: arg2 = " + params["arg2"])
</function>

you can call it with:

	  player.pdict = NewDictionary()
dictionary add (player.pdict, "arg1", "dictionary parameter 1")
dictionary add (player.pdict, "arg2", 666)
DictFunction(player.pdict)

Again, you can separate out the dictionary creation from its use in the function call - and you can overwrite the attribute as you wish.

In summary, I would generally go with a dictionary to hold aggregate values. The downside to dictionaries (everything has one in Quest, sadly) is that it's a pain to *update* a value in a dictionary - you have to first delete an existing value before adding a new one. In that way objects are easier to use.

It all comes down to what your use case is. If you could be more specific about how you will be doing things, then I could offer more targetted guidance.

I've attached a sample project which contains the above code (the script is in the game start script) if you wish to see it in the GUI view.

magano
Umm... I haven't seen that game yet, but it's pretty simple and straightforward
(I added some more, just realised what's needed)
I wanted the status attribute to display 2 or more values like
HP: 30/30 (2 values, one for the Current and one for the Max)
Or
O2 Level: 100% (272.3 ml)

However, I wanted to make that a bit advanced like
Days: 27, Centurion 38 (2 values, no. Of days (27) and the date (38))
Or
Cash: 280 In Hand, 300 In Bank (2 values, in hand money and in bank money)
Or
Time: 07:38:50 (3 values, hours, minutes and seconds)

How to do just that?

jaynabonne
Instead of trying to use two values in the status output, I think you'd need to have a single, third *string* status attribute for that slot which is the formatted output for the other two. And whenever one of the other values changes, you'd need to update that "display" attribute. You could do that with "changed" scripts, so that it would be updated automatically. (Check out this posting by The Pixie about how to do that: viewtopic.php?f=18&t=5307)

Taking your cash example above, you'd have your "cash in hand" attribute, your "cash in bank" attribute, and then there would be an "overall cash" string attribute, which would be the one displayed and which you'd create from the other two values whenever they change.

(It would be an ideal addition to Quest to allow a status attribute to be a delegate returning a string... Then it would run the delegate code and allow you to dynamically build the string each time.)

HegemonKhan
You need a String Attribute for this, and use either a Turnscript or the special 'changed' Script, for example:

Using the special 'changed' Script:

<object name="player">
<attr name="life_string_attribute" type="string">999/999</attr>
<attr name="maximum_life_integer_attribute" type="int">999</attr>
<attr name="current_life_integer_attribute" type="int">999</attr>
<changedcurrent_life_integer_attribute type="script"><![CDATA[
player.life_string_attribute = "Life: " + player.current_life_integer_attribute + "/" + player.current_life_integer_attribute
if (player.current_life_integer_attribute > player.maximum_life_integer_attribute) {
player.current_life_integer_attribute = player.maximum_life_integer_attribute
}
if (player.current_life_integer_attribute <= 0) {
msg ("You died or were killed.")
msg ("GAME OVER")
finish
}
]]></changedcurrent_life_integer_attribute>
<changedmaximum_life_integer_attribute type="script">
player.life_string_attribute = "Life: " + player.current_life_integer_attribute + "/" + player.current_life_integer_attribute
</changedmaximum_life_integer_attribute>
<statusattributes type="simplestringdictionary">life_string_attribute = !</statusattributes>
</object>

// outputs (initially and will change~adjust appropriately):
//
// Life: 999/999


or via Turnscript:

<object name="player">
<attr name="life_string_attribute" type="string">999/999</attr>
<attr name="maximum_life_integer_attribute" type="int">999</attr>
<attr name="current_life_integer_attribute" type="int">999</attr>
<statusattributes type="simplestringdictionary">life_string_attribute = !</statusattributes>
</object>

<turnscript name="global_turnscript">
player.life_string_attribute = "Life: " + player.current_life_integer_attribute + "/" + player.current_life_integer_attribute
if (player.current_life_integer_attribute > player.maximum_life_integer_attribute) {
player.current_life_integer_attribute = player.maximum_life_integer_attribute
}
if (player.current_life_integer_attribute <= 0) {
msg ("You died or were killed.")
msg ("GAME OVER")
finish
}
</turnscript>

// outputs (initially and will change~adjust appropriately):
//
// Life: 999/999


-----------

and take a look at this sample game:

viewtopic.php?f=18&t=4988

in particular, look at how I set up and display these:

Name: john doe smith
Height: 6 feet 6 inches (78 inches)

**********
HK edit: oops this game code was my original, it doesn't have the height in it, nor does it have the middle name. argh. sorry about that... I'll have to to update it with my newer code-work...
***********

create a new game, right click on the game file itself, and open it with notepad, delete the entire code (we want it completely blank), highlight copy paste my code into your game file, save your game file, and then play~study it (in GUI~Editor and~or in code and~or during game play).

errr... or just download my game file (forgot I made it be an attachment too, lol)

let me know if it doesn't work, and I'll get it working for you.

magano
Thanks HK, I'll look into that later on when I'm on my laptop and Quest
Well, in the end, you need a string to do just that...

HegemonKhan
yep, the String Attribute enables you to do your desired display expression, so it is a String Attribute, that you add to the status attributes (to display it), and the String Attribute is set up (using your individual Attributes, such as 'maximim life~hp' and 'current life~hp Integer Attributes, along with the text you need~want) for how you want it to be displayed.

the other trick, is that you need quest to input-update the status attribute~pane, with the changed~altered new life (such after being hit by a monster), which is done via a Turnscript or the special 'changed' Script (and I think there's also a 'core' code command, aka a 'Request' ~ http://docs.textadventures.co.uk/quest/ ... quest.html ~ 'initialize~update status attributes' but I don't see it...)

------------------

HK edit:

nevermind, I found it... just was looking at the wrong place for it in the wiki, lol:

http://docs.textadventures.co.uk/quest/ ... ments.html

'UpdateStatusAttributes' Function

good thing I remembered it... sometimes HK has good memory... not often though, lol

magano
I used your code, applied it to my game and it worked brilliantly!
Thanks for the help!

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

Support

Forums