Making an exception to a trait

Suppose I have a (puzzle $) trait for things that are puzzles. I want all NPCs to be puzzles:

(puzzle *(animate $))

However, I need the player to be animate, but not a puzzle.

#player
(animate *)
~(puzzle *)

Except—this doesn’t work. Now if I make a multi-query to *(puzzle $) to iterate over all puzzles in the game, the multi-query terminates when it hits #player, because the negated rule head is equivalent to (just) (fail).

What’s the proper way to do this? Right now I’m adding the player as an exception to the “animate things are puzzles” rule, but this seems like the wrong solution.

1 Like

I don’t have an answer for this (sorry!) but I did something similar in my own game and ended up making every item in my Dialog game alive. I felt like I was living in the castle in The Beauty and the Beast.

If I had to stab at a solution, I would make a new predicate/trait NPC that applies to animate things that aren’t the player, something like:

(puzzle *(NPC $))

(NPC $obj)
    (animate $obj)
        ~(#player $obj)

But it’s been a while since I tried Dialog, so this may not work at all. If it doesn’t work and wastes your time, I’ll boot up the compiler to make it up to you! (unless someone else figures it out first).

Edit: My impression of Dialog is that it works like magic and you can put if statements anywhere, which was my design idea in the above code.

1 Like

Yours is the easiest solution actually, with regards to the extra lines of code needed:

(puzzle *(animate $Character))
	(current player $Player)
	~($Player = $Character)

Though I think mathbrush’s solution is better, conceptually. (Don’t forget to add the multiquery on the (animate $obj) line for backtracking.)

Couldn’t you also just do

(puzzle *(animate $Char))
    ~(current player $Char)

?

Yes of course, that’s even better. What was I thinking?

1 Like

I think the “standard dialog answer” is to reverse the line order… if you have the specific negation for the player above the general case it seems to work out as you’d hope?

1 Like

For the negation to work it has to come before the general case. But the problem is (just) (fail) will discard the choice point, therefore it will cut short the backtracking and *(puzzle $P) won’t guarantee to iterate over all animate objects.

1 Like

Ah, indeed - I misunderstood the issue. Sorry for the confusion!

One other possibility (to address what I now understand to be the actual problem :sweat_smile:) is a layer of indirection:

Roughly, the outer multiquery is finding candidates and the inner non-multi / “regular” query lets you filter things down.

But in this case I’d almost certainly just do what the other commenters suggest… a special case seems fine.