have_familiar() buggy?

Linknoid

Member
I was writing a script which involves grabbing a daily outfit if it's missing. I have one character with the orphan, and the rest of them don't. It went into an infinite combat loop when testing on a character without the appropriate familiar. So I did some testing:

familiar orphan = "li'l Orphan Tot".to_familiar();
if (orphan.have_familiar())
{
print("Have familiar");
}

This prints "Have familiar" even on characters without the familiar. Am I doing something wrong, or is this a bug? (Just tested using build 17885).
 

lostcalpolydude

Developer
Staff member
> ashq familiar orphan = "li'l Orphan Tot".to_familiar(); print( orphan )

none

If you had done
Code:
$familiar[li'l Orphan Tot]
instead of to_familiar() on a string, the error would have been easier for you to find.
 

Veracity

Developer
Staff member
You are doing something wrong but perhaps there is also a KoLmafia bug.

Code:
familiar orphan = "li'l Orphan Tot".to_familiar();
print( "familiar \"" + orphan.to_string() +"\" id = " + orphan.to_int() );
if (orphan.have_familiar())
{
    print( "Have familiar." );
}
yields

Code:
[color=green]> fams.ash[/color]

familiar "none" id = -1
Have familiar.
Your bug is that "li'l orphan tot" is the name of the item that turns into a familiar called "Trick-or-Treating Tot".
If you do to_familiar on something that is not actually the name of a familiar, you get $familiar[none].

Our (possible) bug is that have_familiar( $familiar[none] ) returns true.
Should it? I don't know.
 

Bale

Minion
No scripter should ever ask have_familiar( $familiar[none] ). There just isn't any sane use for that. Anyplace that I might have a value of none (for familiar, item, etc) , there's always a check to see if it is none so that I can treat the absence of something appropriately.

Is it a bug to say that the character has an item or familiar of none? Is there an equivalent use case for null values in java? If so, use their answer since ash is based loosely on java.
 

Linknoid

Member
I would expect to get back a false or throw an exception in a case like this (obviously I don't have a familiar that meets that criteria), but the bug is a result of my own stupidity, so it's probably not that important. Lesson learned. Thanks!
 

Veracity

Developer
Staff member
If you don't have an equipped familiar, my_familiar() returns $familiar[ none ].
have_familiar() looks at the list of owned familiars in KoLCharacter - there actually is a familiar on that list with id = -1 and name = "(none)". That list is used as it is in the Equipment Manager for the Familiars dropdown. It's also needed so that you have a list (even if you literally have no familiars) and to avoid having to do null checks all over the place.

I could easily make have_familiar() return false for $familiar[ none ]. It seems of little value, but it might be the correct result if you uselessly ask for it.
 

Bale

Minion
It seems you've answered the question to your own satisfaction. Should I open a bug report for the matter?
 

Linknoid

Member
I just realized that this could be a potential breaking change. Someone could have a function which swaps familiars by doing a have_familiar() check before each switch, and they might be passing an explicit "none" as a way to remove their familiar. I think now that I understand how the language is structured, it's a lot more ambiguous whether you should be considered to have "none". It actually makes sense that you might want to equip a "none" value.

I think the confusing part is where an invalid value automatically gets translated to "none" as a legitimate value. "none" is overloaded to mean both "missing value" and "empty value". Now that I'm aware of this, I can account for it in the future.
 

Bale

Minion
There is nothing overloaded about the meaning of $familiar[none]. It is not a missing value at all. use_familiar($familiar[none]) is a logical way to unequip the current familiar without equipping a new one. That should have absolutely no bearing on Veracity's intended fix.
 

Linknoid

Member
Let's say you prompt the user to enter a familiar name. If they type "none", that's a legitimate familiar to equip, they want to unequip the current familiar. But if they mistype a familiar name, that doesn't mean they're trying to equip "none", it means they made a mistake. But the only way to differentiate the two scenarios is to do a string comparison to "none".

Does that make sense? If they want to check if they can equip "none", that seems perfectly legitimate thing to ask, and the current behavior makes more sense.
 

Theraze

Active member
So test for it. You've already given your workaround for how to not mess yourself up if you're doing bad coding on purpose, and Veracity's fix will let you know when you screwed up because of a lazy string to familiar conversion.
 

Linknoid

Member
I'm not worried about myself. I don't have years of existing code to account for. I'll code as defensively as I need to now that I understand it. I'm saying "I'm sorry for complaining it didn't work. It makes sense now that I understand it, and I hope existing scripts don't break because of this change."

I apologize if my communications are unclear, it seems like the more I try to explain, the more misunderstanding I cause.
 
Top