Open Challenge: Repair Object

Overcat
20 Jan 2007, 16:49
!library
!asl-version <400>

'parameter1 = MasterObject name
'parameter2 = Location to create it
define procedure <CloneObject>

set string <pn1; $parameter(1)$>
set string <pn2; >
set string <pn3; $parameter(2)$>
set numeric <pn1; 0>
set numeric <pn2; 0>

repeat until ( %pn2% = 1 ) {

inc <pn1>
set string <pn2; #pn1#_%pn1%>
if not real <#pn2#> then {
inc <pn2>
if type <#pn1#; Item> then clone <#pn1#; #pn2#; #pn3#> else clone <#pn1#; #pn2#; #quest.currentroom#>
}
else {
if ( $locationof(#pn2#)$ = scrapyard ) then {
inc <pn2>
do <RepairObject(#pn2#)>
msg < Object found in scrapyard (#pn2#).>
if type <#pn2#; Item> then move <#pn2#; #pn3#> else move <#pn2#; #quest.currentroom#>
}
}
}

end define

'parameter1 = object to repair
define procedure <RepairObject>

' execute all repairs here, such as object condition and inherited types, etc.
' would be great if there were a 'for each property on <#object#>' or 'for each action on <#object#>'

'IE
'
'set string <object; $parameter(1)$>
'set string <master; #(object):master#>
'
'for each property on <#object#> {
'
' if property <#master#; #quest.property#> then property <#object#; #quest.property# = $objectproperty(#master#; #quest.property#)$> else property <#object#; not #quest.property#>
'
'}
'
'for each property on <#master#> {
'
' property <#object#; #quest.property# = $objectproperty(#master#' #quest.property#)$>
'}
'
'same for actions, with a #quest.action# built-in string
'same for types, with a #quest.type# built-in string

end define

define room <scrapyard>

end define


I stumbled upon something interesting while working away at my game. Because it's an RPG/IF hybrid, there are some gamist elements to it, such as buying/selling equipment, item cleanup, etc. This means that there may be a lot of unused objects in the game that the player, at any given point, might not have been interested in. Those objects I send to a room called 'scrapyard'. For instance, if the player defeats an enemy, combs through their belongings and leaves a short sword behind, that short sword is sent to the scrapyard.

Now if the player at a later time wants to buy a short sword, I thought it would be prudent to check if there are any unused short swords out there before creating a new one. That's essentially what the CloneObject procedure does. I've created blueprints, or master objects, for every non-unique item in the game. The player never touches those - she only comes into contact with objects that are cloned from the master. If the player buys a short sword, the CloneObject procedure attempts to create one with a sequential identifier (number). IE, short_sword_1. If it doesn't exist, the procedure clones one from the master object, short_sword. If the object does exist, and is not located in the scrapyard, then it is being used elsewhere. (The identifier increments, and we repeat.) If the object exists and is located in the scrapyard, then the player receives that object instead, since it's not being used.

Here's the challenge. I need an efficient way to restore the scrapped object's properties and actions to it's master's properties and actions. The scrapped short sword may be damaged, have had types (like flammable) added to it, or types removed. This may just turn into a feature request:

1. for each property on <object> - stored in #quest.property#
2. for each action on <object> - stored in #quest.action#
3. for each type on <object> - stored in #quest.type#

I think Im Dead
21 Jan 2007, 02:42
I have to say I love the way you think because this is almost exactly how I've implemented shops/items in a few pre-alpha libraries over the years. It was a bit difficult when Quest was a bit younger, but no big deal now if I'm getting your drift completely. Currently I'm a bit tied up but I'll try and bang something out a bit later.

From a quick glance, with a little work around and extra footwork on base items, you can implement a custom function to iterate through objects properties as I'm sure you can imagine.

paul_one
21 Jan 2007, 12:18
Right... First off, how have you done properties such as "flammable" (I'd guess "poison" or "slow" are also effects/properties like these).

I know in mine, I gave it an "effects=##" way of doing things... Sure you may need to parse the comma/anything (I think I used '|'s or '\'s) BUT you make it hella easier to restore defaults...

In my alpha, I used something like so:

 startscript {
set string <props[1]; hp>
set string <props[2]; mp>
set string <props[3]; fire>
set string <props[4]; effect>
...
}

This is an array of the object properties which effect: monsters/magic/equipment/YOUR status.

Then, to just restore it, you can use this:
set string <obj1; deafult_obj_1>
set string <obj2; scrapped_obj_1>
for <iter;1;$ubound(props)$> {
property <#obj2#; #props[iter]#=$objectproperty(#obj1#:#props[iter]#)$>
}


I actually did some calculations, additions and subtractions for stat purposes (because writing out 15 lines of code when a loop like this did it in four, was just silly IMO).
.. This will also reset it if your "effects" property is equal to stuff.

If you're far too gone - then you could always do a more complicated loop:
for <iter;1;$ubound(props)$> {
set string <property.value; $objectproperty(#obj1#;#props[iter]#)$>
if ( #property.value# = ! ) then {
if property <#obj1#;#props[iter]#> then _
property <#obj2#; #props[iter]#>
}
else property <#obj2#; #props[iter]#=$objectproperty(#obj1#:#props[iter]#)$>
}


.. I think that's correct - although I haven't double-checked.

Overcat
21 Jan 2007, 12:24

...as I'm sure you can imagine.



Damn, my imagination needs a kickstart, then.

I have thought about each base item having its own RepairClone action. This would check the properties/actions/types it knows about. But what about properties, actions, and types subsequently added to the clone that the base item knows nothing about? I need to remove them. I'd have to hard code an 'if' statement for each of the possiblities, which, at this point, look like there will be many of. Every time I implement a new possiblity, I'd have to go through all the base item RepairClone functions to see if they should check for it or not.

Can't wait to see what you come up with, ITID.

Or anyone else: I know there are lots of good thinkers here.

Overcat
21 Jan 2007, 12:36

Right... First off, how have you done properties such as "flammable" (I'd guess "poison" or "slow" are also effects/properties like these).



I was going to type them. Add the properties and actions as a type.

I see the value in the method you've described, however, especially for efficiency. How would I remove properties that the cloned object should no longer have? For instance, what if I gave it a Glowing property that now I want to remove? Would every item, in effect, have a Glowing parameter that, if not above 0, indicated it was not glowing? Yeah, that would work. I surmise this is what you are doing.

This would also mean that all of the actions I code for every effect would be attached to every object. I would have to check if they had the correlating property value in order to execute the action. IE, no longer "if action <#object#; action> then doaction <#object#; action>', but 'if ( #(object):actionproperty# > 0 ) then doaction <#object#; action>'.

Do I have this right?

paul_one
21 Jan 2007, 21:03
Right, I forgot to add in a second part for the second if..

.. if you see - I check to see if an object's property has a value.
I presume that if an object property is "alive" or "dead" or "green" or "flammable" then trying to get a value will result in "!" coming back (it may not).
If this is true then there's two possibilities:
1) the property IS there - but no value
2) the property is not there.

.. So I only checked for number 1.. So the if should be:

if property <#obj1#;#props[iter]#> then _
property <#obj2#; #props[iter]#> _
else _
property <#obj2#; not #props[iter]#>

This removes any property in the array "props" (such as "glowing" etc).

For your "type" stuff, etc (personally I wouldn't do it that way - but it's cos I don't like types in Quest) then you can have several properties/arrays combinations to allow you to dynamically set stuff to defaults.
The first which comes to mind is simply this:
property <props=HP\MP\fire\glowing\poison\whatever; acts=repell\destroy\throw...>

These will allow you to loop through all the properties or actions an object can have dynamically - as they are added/removed.
Of course - this doesn't work with types, as applying a type will overwrite the existing properties.. so you'd have to add them in manually...
(although - the thought I just came up with was a "new_type" object action, which can add in the relevant properties when you add in the type - it could even randomize/set values of properties)

Personally - I only see this being typable - or hugely dynamic - if you can create your weapons from scratch - or HUGELY customize them..

Overcat
22 Jan 2007, 01:11
Yes, I see. That is definitely doable in a small amount of time. I think I'll try something along that line for now. Thanks, tr0n.

paul_one
22 Jan 2007, 10:33
I employed something similar to the "actions=eat\kill\fireball" approach with monsters.
Monsters of different types (say bosses etc) could have different actions. Those actions may be wanted to fire off in a certain proportion to the other actions.. So "attack" might be used 20% of the time, while "fireball" might be 70.. And "explosion" 10%.

So I had a ratio as well... "rat=3\17\30\50". A range is split up accordingly and a random number picked.

I also did this sort of stuff for the monsters.. I didn't want an infinite amount of monsters - so I reused them - and restored the HP and other status stuff back to normal as much as possible.. This was back in 2003 or 2002 sometime.

.. I didn't do it for items though - mainly as I only do single player, etc.

Overcat
22 Jan 2007, 12:18
Yeah, I haven't sprung too much into monster/hostiles coding yet. But what you're talking about is obviously something that has to be dealt with. Up until this point, I was thinking of coding several different AI scripts of various intensities. That is, dummy_AI all the way to genius_AI, then assigning a property called CombatScript to each hostile pointing to the relevant AI script. (This way I can dynamically change the script. If they are severely wounded, or hit with a confusion spell, that script may need to be changed.)

To help the AI, I was going to store the last several actions of each combatant as an array on that combatant. That way, combatants can 'look up' the history of their own actions and the history of their opponents(') actions to determine more logical next moves.

To tie it all up, I certainly was going to employ some sort of randomized action selection inside of the logic. The dummy_AI script would use a hell of a lot more random selection than the genius_AI script. Assigning ratios and actions as two properties would seem to work out well for that.

Hope I'm not missing anything. It always seems that, as you go along, a simpler way shows itself AFTER you've finished the more complicated way.

paul_one
23 Jan 2007, 10:23
.. To be honest, I always think into several different ways, and try to think of all the tiny quirks eacdh could have.. Sometimes I also draw out small diagrams of how they appear logically in my head so that I can understand it a little better.

Half the times it pays off as I can look at several differences and choose the better one.

On a side note, you are concentrating on the more correct part at the moment. A general idea going around - of which I agree - is that a good programmer worries about the code, but a GREAT programmer worries about the data structure.

Overcat
23 Jan 2007, 12:50

a good programmer worries about the code, but a GREAT programmer worries about the data structure.



Ooh, I'm about to google that. Or, you could explain the idea in a little more detail, since my curiousity is piqued.

paul_one
23 Jan 2007, 17:31
Erm, the actual quote is:

I will, in fact, claim that the difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.

From Linus Torvalds.. I actually think it's quite right.

He's come up with quite a few good quotes, seems like a very funny chap.

007bond
24 Jan 2007, 05:11
Linus Torvalds?

He was the guy who wrote Linux. He's a really smart guy

paul_one
24 Jan 2007, 15:47
He is still developing the kernel, along with several others.

Just because he's a big name, doesn't mean he's 'smart', or technically always "right".