Remapping actions, default message outputs and... stuff.

This is long and… miscellaneous. I have three issues, the first two are probably dumb and quick but require explanation of what I’m doing. The third one I don’t know where to begin…

First problem: Remapping actions to other actions:
[rant]I wanted a small water spring from which you could drink:

spirngArea: OutdoorRoom 'Forest Spring' ; +spring: Fixture 'divine holy water/spring/pool' 'water spring' "A water spring" ;

Then I messed with the dobjFor(Drink) so you could actually drink from it:

+spring: Fixture 'divine holy water/spring/pool' 'water spring' "A water spring" dobjFor(Drink) { preCond = [] verify() {} action() { "You drank from the spring. "; } } ;
THEN I realized I wanted to make sure additional verb inputs like “drink from spring” and “drink water from spring” worked, so I created two new actions:

[code]
DefineTAction(DrinkFrom)
;

VerbRule(DrinkFrom)
(‘drink’ | ‘quaff’ | ‘imbibe’)
(‘from’ | ‘out’ ‘of’ | ‘off’ | ‘off’ ‘of’) dobjList
: DrinkFromAction
verbPhrase = ‘drink/drinking (what)’
;

DefineTIAction(DrinkXFrom)
;

VerbRule(DrinkXFrom)
(‘drink’ | ‘quaff’ | ‘imbibe’) dobjList
(‘from’ | ‘out’ ‘of’ | ‘off’ | ‘off’ ‘of’) singleIobj
: DrinkXFromAction
verbPhrase = ‘drink/drinking (what)’
;[/code]

Aaaand here’s where I probably went crazy: The only way I could figure out for these two actions to have the exact same effect as Drink was to copy the exact same code three times, so the spring object ends up looking like this:

+spring: Fixture 'divine holy water/spring/pool' 'water spring' "A water spring" dobjFor(Drink) { preCond = [] verify() {} action() { "You drank from the spring. "; } } dobjFor(DrinkFrom) { preCond = [] verify() {} action() { "You drank from the spring. "; } } dobjFor(DrinkXFrom) { preCond = [] verify() {} action() { "You drank from the spring. "; } } ;

I get the feeling that there should probably be a tidier way to do this, but I can’t figure out how to make Remap work for me here.[/rant]

Second and third problems are about Wearables, or rather my custom class of Wearables, called Equipment. This first one has to do with default message responses. Here’s the gist of the code:
[rant][code]class Equipment: Wearable
dobjFor(Wear)
{
preCond = [objHeld]
verify()
{
if (isWornBy(gActor))
illogicalAlready(&alreadyWearingMsg);
}
action()
{
equipAlterStats();
makeWornBy(gActor);
defaultReport(&okayWearMsg);
}
}
dobjFor(Doff)
{
preCond = [roomToHoldObj]
verify()
{
if (!isWornBy(gActor))
illogicalAlready(&notWearingMsg);
else
logicalRank(150, ‘worn’);
}
action()
{
unequipAlterStats();
makeWornBy(nil);
defaultReport(&okayDoffMsg);
}
}
equipAlterStats()
{
gPlayerChar.ATK += ATK;
}
unequipAlterStats()
{
gPlayerChar.ATK -= ATK;
}
;

class Weapon: Equipment
okayWearMsg = "You are now wielding <>. "
alreadyWearingMsg = "You are already wielding <>. "
notWearingMsg = "You are not currently wielding <>. "
okayDoffMsg = "You are no longer wielding <>. "
;

dagger: Weapon ‘steel small rusted knife/dagger’ ‘rusted dagger’ @springArea
"It’s a small rusted dagger. "
ATK = 10
;[/code]

The main point of this mess is to have the (still very unfinished) equipAlterStats() and unequipAlterStats() methods executed when the objects are equipped or unequipped, thus having the typical effect of RPG equipment: altering stats. The point of the subClass Weapon is to customize the messages according to the type of object (You wield a weapon, but you’d wear an armor and so on). It all works pretty well except for illogicalAlready(&alreadyWearingMsg) and illogicalAlready(&notWearingMsg), since these already print their own “you are already…” messages, so the output ends up looking as:

equip dagger
(first taking the rusted dagger)
You are now wielding the rusted dagger.

equip dagger
You are already wielding the rusted dagger. You’re already wearing it.

unequip dagger
You are no longer wielding the rusted dagger.

unequip dagger
You are not currently wielding the rusted dagger. You’re not wearing that.

The parts in bold are redundant and I can’t figure out how to get rid of them.[/rant]

The third issue… I don’t even know where to begin. The aim here is to no allow for more than a single sample of each type of equipment to be equipped at once. So if an item from the Weapon class (like the dagger above) is equipped and the player tries to Equip another Weapon item, like say:
[rant]sword: Weapon 'steel rusted sword/blade' 'rusted sword' @springArea "It's a small rusted blade. " ATK = 10 ;

I’d want the game to automatically unequip the dagger and equip the sword.

The macro bcressey showed me before…

#define MakeClassList(K, N) \ ##N : object \ \ lst() \ { \ if (lst_ == nil) \ initLst(); \ return lst_; \ } \ \ initLst() \ { \ lst_ = new Vector(50); \ local obj = firstObj(); \ while (obj != nil) \ { \ if(obj.ofKind(##K)) \ lst_.append(obj); \ obj = nextObj(obj); \ } \ lst_ = lst_.toList(); \ } \ \ indexOf(val) \ { \ if (lst_ == nil) \ initLst(); \ return lst_.indexOf(val); \ } \ \ indexWhich(func) \ { \ if (lst_ == nil) \ initLst(); \ return lst_.indexWhich(func); \ } \ \ subset(func) \ { \ if (lst_ == nil) \ initLst(); \ return lst_.subset(func); \ } \ \ lst_ = nil \

…allows me to create a list with all Weapon-class objects via MakeClassList(Weapon, allWeapons), but I can’t figure out a way to use it in away that I could add a check so that, upon trying to execute the (customized above) dobjFor(Wear) action of one of these Weapons, it’d check if any other objects of its class are already being worn, and if so, would execute that respective object’s (customized) dobjFor(Doff) action. If that’s not doable, I’d settle for simply stopping the Wear action and having the player manually unequip the Weapon before equipping the new one. This is… probably because I don’t actually understand how to use the macro’s lists, I just rolled with the code bcressey handed me already made when I used it for rooms and now I’m paying the price of using code I don’t even understand fully.[/rant]

My T3 is extremely rusty … but with respect to your third query, you might look at p. 140 of “Learning T3.” That page might suggest code like the following (totally untested and may not compile even if you substitute your own class and method names):

foreach (local cur in me.contents) { if (cur.ofKind(sameWeaponClass) { cur.specialDoffMethod(); } }

For the first issue, it’s trivial to remap one action to another when they are both TAction.

modify Thing
    dobjFor(DrinkFrom)    remapTo(Drink, self)
;

It gets slightly more complex when you are mapping a TIAction to a TAction, because then you are discarding some information. I would make all your springs, fountains etc inherit from a common class to simplify the remapping.

[rant] // this code kind of works, but see below for a better way class WaterSource: Fixture iobjFor(DrinkXFrom) remapTo(Drink, self) ; [/rant]

Here you just want to make sure that every logical thing that the player could drink from the source is included in the vocabWords for that source. So >DRINK WATER FROM SPRING would be the same as >DRINK SPRING FROM SPRING, and you can happily collapse it to >DRINK SPRING without any risk.

When the player types something random like >DRINK CHERRY COKE FROM SPRING it should fail with an appropriate message. But if they enter >DRINK GLADIUS SWORD FROM SPRING and the player has that sword equipped it will accept the input.

If that bothers you, you have to handle this on the direct object instead of the indirect object:

modify Thing
    dobjFor(DrinkXFrom)
    {
        remap = (isDrinkable ? [DrinkAction, gIobj] : nil)
        verify() { nonObvious; }
        check() { failCheck('You wouldn\'t know where to begin. '); }
    }
    isDrinkable() { return nil; }
;

class WaterSource: Fixture
    isDrinkable() { return true; }
;

This should fail >DRINK GLADIUS SWORD FROM SPRING and allow >DRINK WATER FROM TREE. The latter collapses to >DRINK TREE which should be covered by the DrinkAction’s default failure messages.

For the second issue, change defaultReport to either mainReport or reportFailure.

The article on transcript manipulation has more details. Essentially all library messages are implemented as defaultReports, and they silently disappear as long as one of the main report types is used.

The way I’ve done this is simply to modify the Actor object to include variables that will be set to the different kinds of equipment you can have. For example, here’s a bit of code from an unfinished project:

[spoiler][code]modify Actor
equippedWeapon=nil
;

class Weapon: Thing
dobjFor(Equip)
{
preCond = [objHeld]
verify(){}
check()
{
if(gPlayerChar.equippedWeapon==self)
{
failCheck(’{You/he} {have} already got that equipped. ');
}
}
action()
{
if(gPlayerChar.equippedWeapon!=nil)
{
gPlayerChar.equippedWeapon.doUnequip();
“<.p>”;
}
equipDesc;
gPlayerChar.equippedWeapon=self;
}
}

doUnequip()
{
    unequipDesc;
    gPlayerChar.equippedWeapon=nil;
}

dobjFor(UnEquip)
{
    if(gPlayerChar.equippedWeapon==nil)
        "You don't have a weapon equipped. ";
    else
    {
        doUnequip();
    }
}
//unequip if drop, place on something etc.
moveInto(newCont)
{
    if(gPlayerChar.equippedWeapon==self)
    {
        doUnequip();
    }
    inherited(newCont);
}
equipDesc="You equip <<self.theName>>. "
unequipDesc="You unequip <<self.theName>>. "

;
[/code][/spoiler]
Obviously, you’ll need to have defined Equip and UnEquip actions for that code to work (and probably some other stuff I’ve forgotten. But the gist of it is that you store the equipped weapon in gPlayerChar.equippedWeapon, and by checking what (if anything) is in that variable we have access to the equipped weapon (for example, to unequip it before equipping a new one).

In another project the PC has a variable for jackets, and I defined the following class:

class Jacket: Wearable dobjFor(Wear) { check() { if(gActor==gPlayerChar && gPlayerChar.jacket!=nil) { reportFailure('You can\'t wear that over your ' + gPlayerChar.jacket.name + '. '); exit; } } action() { gPlayerChar.jacket=self; inherited(); } } dobjFor(Doff) { action() { gPlayerChar.jacket=nil; inherited(); } } ;
I think this takes advantage of how Doff is always called before dropping or moving something you’re wearing.

Anyway, hopefully you get the idea.

Took me a while to implement all that, but I got it all to work. Thank you all for your assistance.