I’m trying to get the verb THROW to work the way I expect it to. One problem I’ve noticed is that the standard library omits a tilde where I would expect one to be in a (before $) rule, but even after accounting for that, I’m still running into a proverbial barrier.
(current player #pc)
(#pc is #in #house)
#pc
#house
(room *)
(name *) house
(from * go #east to #yard)
(look *)
You are in the house.
#softball
(item *)
(name *) softball
(dict *) soft ball
(* is #in #house)
(appearance *)
You see (a *) here.
#yard
(room *)
(name *) front yard
(from * go #west to #house)
(grammar [throw [single] [direction]] for [throw $ $])
(before [throw $Obj $Dir])
(direction $Dir)
(current player $Actor)
~($Obj has ancestor $Actor)
(ensure $Obj is held)
~(before [throw $ $])
(perform [throw $Obj $Dir])
(direction $Dir)
(current room $Here)
(from $Here go $Dir to $Target)
(now)($Obj has parent $Target)
You throw (the $Obj) (name $Dir) .
~(instead of [throw $ $])
The transcript:
> throw ball east
(first attempting to take the softball)
You take the softball.
> throw ball east
The problem is disabling (instead of $). The default rule for (instead of $) is what calls (prevent $), (perform $), and so on, so if you block that rule, the action will never be performed.
Is there a reason you’re turning off (before $) and (instead of $) for this action?
By far the easiest solution is to remove the ~(before [throw $ $]) and ~(instead of [throw $ $]) rules in your code and comment out this block of code in the standard library:
(instead of [throw $Obj $Dir])
(current room $Here)
(if) (from $Here go $Dir to object $Target) (then)
(try [throw $Obj at $Target])
(else)
(try [throw $Obj])
(endif)
When the action doesn’t get redirected by this instead rule, the before rule you’re trying to stop never triggers for the [throw $Obj $Dir] action, so it’s not necessary to comment it out. This solution also allows other before or instead rules to trigger. Do you have a particular reason for not modifying the standard library?
Additionally, you might want to change the (perform [throw $Obj $Dir]) rule so that it states (now)($Obj is #in $Target) rather than (now)($Obj has parent $Target). As it is, the softball doesn’t end up in scope for #yard once it’s been thrown east.
In the standard library (before $) succeeds if and only if the object has the PC as an ancestor. Thus, it will fail if the PC is not holding the object. Presumably, that is not what was intended.
As for (instead of $), the standard library’s rule has THROW <object> <direction> redirect to a rule that throws the object at a target. I want the object to instead end up in the room in the specified direction.
In hindsight I should have led with the default behavior, as my motivations are now opaque apparently.
If I had preserved the standard library’s behavior, this is what the player would see when they try to throw the softball without having first picked it up:
> throw ball east
You're not holding the softball.
Presumably, that isn’t what the author of the relevant portion of the standard library intended.
If one makes a minimal modification to the standard library so that ($Obj has ancestor $Actor) is preceded with a ~, the player sees this instead:
> throw ball east
(first attempting to take the softball)
You take the softball.
Throwing the softball at the front yard would achieve little.
Now the (before $) rule works as expected.
Attempting to throw the ball at the yard isn’t as obviously undesirable as the current behavior of the standard library’s (before $) rule. All the same, I’d prefer it if the PC threw the ball into the yard instead.
The instead rule that I suggested you remove is the one responsible for redirecting the action from “throw ball east” to “throw ball at yard.” Once it’s been commented out, your perform rule is the one that will get used when you try to throw the ball in a particular direction.
Each of you—you and Daniel—has offered a perfectly working solution to one of the problems I’ve brought up in this thread. Unfortunately, these solutions are not compatible. If the (instead of $) rule in the standard library is commented out, there will be no chain of succeeding predicates that results in a call to the (before $) predicate.
There is, of course, a simple way to make these fixes work in tandem, namely creating an additional (before $) rule that is exactly like the other rule except that it accepts input of the form throw <object> <direction>. But it strikes me as inelegant.
Thank you for the suggestion. I want to think about this more. Every time I look at the relevant code in the standard library, I lean further in the direction of thinking a more extensive change is necessary. As much as I love that Dialog makes it easy for the author to edit the standard library (Linus really succeeded wonderfully in that respect), I feel that the author shouldn’t have to do so unless making dramatic changes to behavior that is par for the course in parser fiction. And in my opinion wanting a ball to go east when someone enters throw ball east does not qualify.
Of course, I’d be interested in hearing what others here have to say on this matter.
By default, the library doesn’t let you move objects between rooms without going there yourself, because this can very easily lead to unwinnable situations. (Imagine if you need to bribe a guard to pass through a checkpoint, but you throw your money through the checkpoint…) We don’t want it to be a trap for new authors who aren’t expecting that behavior.
I think I figured out a way to accomplish what you’re describing. You’ll still need to get rid of the instead rule, but you can have a single before rule that runs before either throw $Obj at $Target or throw $Obj $Dir:
(before [throw $Obj | $Tail])
(nonempty $Tail) %% This keeps the rule from triggering for [throw $Obj]
(current player $Actor)
~($Obj has ancestor $Actor)
(ensure $Obj is held)
This only works assuming there are no actions starting with throw $Obj (aside from [throw $Obj] itself) for which you don’t want this rule to run. If there are, you’d want the before rule to look something like this instead:
(before [throw $Obj | $Tail])
{($Tail = [$Dir])(direction $Dir) (or) ($Tail = [at $])}
(current player $Actor)
~($Obj has ancestor $Actor)
(ensure $Obj is held)
You could also have this rule run for just plain [throw $] as well; it’s hard to imagine a situation where someone wants to throw something that’s not in their hands.
True; the only difference is whether the PC picks up the item before or after specifying what they want to throw it at. I was mimicking the behavior of the standard library, which has a before rule for throw [$Obj at $Target] but not for [throw $Obj].
I’m not questioning the goal so much as I’m questioning the path. As an author, I’d prefer it if the library was written in such a way that it simply prevented the action from being carried out, allowing the author to write a (perform $) rule to permit behavior like what I want if they choose to do so.
That’s a very reasonable way to do it! If you want to propose a change to the library (say something like “you don’t want to lose the $Obj” or whatever), I think that would be a fine one to make.