ConSpace Usability

Anyone have any issues compiling the ConSpace library (specifically ConSpace2/Space_un_us.t), under linux/t3make? All other components seem to compile fine, or at least without coredump. Have already tried dos2unix, and internal syntax triangulation has not yielded fruit yet. My gut says it is most likely more of this: TADS Won’t Compile, but thought I’d ask before I start line-by-lining it.

1 Like

I looked at using ConSpace2 and didn’t run into any problems either compiling the test game or compiling it against my own code (although I subsequently decided to not use it).

1 Like

Just for reference, here’s a scrap of nonsense I put together when I was looking at it, implementing an annoying-to-navigate road location:

#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>

roadGroup: SpaceConnector;
roadGroupNorth: roadGroup;
roadGroupSouth: roadGroup;

class RoadRoom: Room
        connectionGroup = [ roadGroup ]
        actorInPrep = 'on'
        actorIntoPrep() {
                if(gActor.locationBefore.getOutermostRoom.ofKind(RoadRoom))
                        return('over to');
                else
                        return('onto');
        }
;
class RoadRoomNorth: RoadRoom
        connectionGroup = [ roadGroupNorth ]
        north = northRoad
        east = northeastSidewalk
        west = northwestSidewalk
;
class RoadRoomSouth: Room
        connectionGroup = [ roadGroupSouth ]
        south = southRoad
        east = southeastSidewalk
        west = southwestSidewalk
;

// North part of downtown
northRoadMain:  RoadRoomNorth 'North End Of Downtown' 'north end of downtown' 'm
iddle of the road'
        "This is a road running north and south.  The road is flanked to
        the east and west by shops. "
        south = southRoadMain
;
+me:    Person;
+pebble: Thing 'small round pebble' 'pebble'
        "A small, round pebble. "
;
northeastSidewalk: RoadRoomNorth 'Northeast Sidewalk'
        "This is the sidewalk to the east of the road.  The Northeast Shop is
        just to the east, and the Northwest Shop is across the road to the
        west. "
        south = southeastSidewalk
        west = northwestSidewalk
        east = northeastShop
;
northwestSidewalk: RoadRoomNorth 'Northwest Sidewalk'
        "This is the sidewalk to the west of the road.  The Northwest Shop is
        just west of here, and the Northeast Shop is off to the east, across
        the road. "
        south = southwestSidewalk
        east = northeastSidewalk
        west = northwestShop
;
northeastShop: Room 'Northeast Shop'
        "This is the Northeast Shop.  The only exit is to the west. "
        west = northeastSidewalk
;
northwestShop: Room 'Northwest Shop'
        "This is the Northwest Shop.  A doorway east opens onto the sidewalk. "
        east = northwestSidewalk
;

// South part of downtown
southRoadMain:  RoadRoomSouth 'South End Of Downtown' 'south end of downtown' 'middle of the road'
        "This is a road running north and south.  The road is flanked to
        the east and west by shops. "
        north = northRoadMain
;
southeastSidewalk: RoadRoomSouth 'Southeast Sidewalk'
        "This is the sidewalk to the east of the road.  The Southeast Shop is
        just to the east, and the Southwest Shop is across the road to the
        west. "
        north = northeastSidewalk
        west = southwestSidewalk
        east = southeastShop
;
southwestSidewalk: RoadRoomSouth 'Southwest Sidewalk'
        "This is the sidewalk to the west of the road.  The Southwest Shop is
        just west of here, and the Southeast Shop is off to the east, across
        the road. "
        north = northwestSidewalk
        east = southeastSidewalk
        west = southwestShop
;
southeastShop: Room 'Southeast Shop'
        "This is the Southeast Shop.  The only exit is to the west. "
        west = southeastSidewalk
;
southwestShop: Room 'Southwest Shop'
        "This is the Southwest Shop.  A doorway east opens onto the sidewalk. "
        east = southwestSidewalk
;

// Outside of downtown
northRoad: Room 'North Road'
        "This is the north road.  Everything interesting is south of here. "
        south = northRoadMain
;
southRoad: Room 'South Road'
        "This is the south road.  Downtown, such as it is, lies to the north. "
        north = southRoadMain
;

versionInfo:    GameID
        name = 'sample'
        byline = 'nobody'
        authorEmail = 'nobody <foo@bar.com>'
        desc = '[This space intentionally left blank]'
        version = '1.0'
        IFID = '12345'
;
gameMain:       GameMainDef
        initialPlayerChar = me
;

…which compiles with…

-D LANGUAGE=en_us
-D MESSAGESTYLE=neu
-Fy obj -Fo obj
-o game.t3
-lib system
-lib adv3/adv3
-lib ../extensions/ConSpace2/ConSpace
-source sample

…and seems to run fine:

North End Of Downtown
This is a road running north and south.  The road is flanked to the east and
west by shops.

You see a pebble here.

>w
You walk over to the northwest sidewalk.

>l
Northwest Sidewalk
This is the sidewalk to the west of the road.  The Northwest Shop is just west
of here, and the Northeast Shop is off to the east, across the road.

On the middle of the road, you see a pebble.
2 Likes

Thanks for the quick confirmation. It is definitely more of THIS

For whatever reason, it coredumps on any VerbRule statements, new, replace or modifies. When I cut all of those statements and paste into another, currently compiling, file the whole mess builds just fine. Interesting that only VerbRule seems vulnerable. Will probe further after I decide if I want ConSpace or not…

Doesn’t appear to be VerbRules in general with ConSpace2.

I just tacked this onto the “demo” above:

DefineSystemAction(Foozle)
        execAction() {
                defaultReport('A hollow voice says <q>This space intentionally
                        left blank.</q> ');
        }
;
VerbRule(Foozle)
        'foozle' : FoozleAction
        verbPhrase = 'foozle/foozling'
;

…and it works fine:

North End Of Downtown
This is a road running north and south.  The road is flanked to the east and
west by shops.

You see a pebble here.

>w
You walk over to the northwest sidewalk.

>l
Northwest Sidewalk
This is the sidewalk to the west of the road.  The Northwest Shop is just west
of here, and the Northeast Shop is off to the east, across the road.

On the middle of the road, you see a pebble.

>foozle
A hollow voice says "This space intentionally left blank."
1 Like

Still no daylight on my local compilation problem, but works fine with chopped up source files. Wanted to document some usability fixes I needed to make. I was primarily engaging to take advantage of Enterable ComplexContainers (ECContainer in the module’s parlance). I noticed two behaviors that seemed slightly off:

  1. When attempting to get in/on/under/behind anything forbidden, the error message was You cannot stand {in/on/behind/under} that. Even for >SIT BEHIND, >LIE UNDER etc The appropriate sit/lie error messages are provisioned in the code.

  2. When attempting to >WALK OVER TO SOMEOBJ in a room without a defined connectionGroup, you got the standard The someobj is too far away. error message. Even though you are in the same room with SOMEOBJ. (Note, without a true ConSpace implementation, >WALK OVER TO doesn’t do much, but according to my sensibilities it shouldn’t FAIL with deceptive message.)

Both issues I traced to the new verifyStandClose method in ConSpace.t Locally hacked fixes below:

  verifyStandClose(pos, post)
  {
    local locProp = whichLocProp(pos);
    if(self.(locProp) == nil) {
    // (1) Modified to get sit/stand/lie messaging correct - JJMcC
    //
      //illogical(&cannotStandCloseMsg, pos);  // original code
      switch(post) {
          case sitting:
            illogical(&cannotSitCloseMsg, pos);
            break;
          case lying:
            illogical(&cannotLieCloseMsg, pos);
            break;
          default:
            illogical(&cannotStandCloseMsg, pos);
            break;
      }
    }

    if(gActor.isIn(self.(locProp))
       && !gActor.roomLocation.ofKind(NestedRoom)
       && gActor.posture == post)
      illogicalAlready(&actorPostureThereMsg, self, pos);

   /* Don't allow this kind of travel to occur unless
    * the object is within the same connectionGroup
    * as the actor, and autoApproach is allowed (true)
    * for this object, unless the actor is in one of the
    * locations specified in my extraApproachRooms list.
    */
    /*  Also allow if in same room!  JJMcC */

   if (((gActor.CSGetOutermostRoom.connectionGroup == nil
       || !inSameConnectionGroup(gActor)
          || !autoApproach)
       && extraApproachRooms.indexOf(gActor.CSGetOutermostRoom) == nil)
   // (2) Needed to add test for same room, as otherwise fails here without
   //  defined connectionGroup! - JJMcC
        && (!gActor.isIn(self.(locProp))))
     illogical(&tooDistantMsg, self);
  }

Other than these glitches, ECContainers do pretty much exactly what I wanted, and even without a true ConSpace implementation, the new verbs are nicely flexible. Think I still prefer Open Door Patterns as lighter to customize than ConSpace’s SpaceConnectorDoor. Though this could just be sunk cost fallacy.

1 Like

And another one, though I’ll grant this one is in the weeds a bit. My situation is this: I have a room, in which is a large stage, modeled as three platforms (which the PC can freely traverse): Upstage, StageRight and StageLeft. On this, I have a very large-bodied Multi-Loc NPC who occupies all three platforms.

Meaning the location nesting for the NPC we’ll call “JollyGreen” is
JollyGreen->BigBody(multiLoc)->All 3 Stages->TheatreRoom

To interact with JollyGreen, the PC must pass the canTalkToObj precondition, which ConSpace.t modifies:

modify canTalkToObj
    checkPreCondition(obj, allowImplicit)
    {
        local actionAttempted = nil;

        if(!obj.CSGetOutermostRoom.allowRemoteConversation
           && obj.CSGetOutermostRoom != gActor.CSGetOutermostRoom)
        {    // fail cases & etc

They can talk, as long as CSGetOutermostRoom shows the same for both of them, in this case TheatreRoom.

CSGetOutermostRoom is defined twice. Once for baseline Thing, and overridden for MultiLocs. For Thing, it recurses up the location tree until it finds the top.

modify Thing  
  /* In most cases, CSGetOutermostRoom() should behave just like the
   * normal getOutermostRoom(). But if there's an intervening multiLoc,
   * in the containment tree, we will perform a search based on gActor's
   * location, and we will return the multiLoc's SCGetOutermostRoom,
   * which is the location in the multiLoc's locationList that is
   * nearest the gActor. (Normally, because multiLoc has no location
   * property, multiLoc returns 'self' for its outermost room.)
   */
  CSGetOutermostRoom() {
    return (location ? location.CSGetOutermostRoom() : self);
  }

/* Modify MultiLoc so that CSGetOutermostRoom returns the room in its
 * locationList which is nearest to gActor's location.
 *
 * If for whatever reason no such path is found, return locationList[1]
 * (or nil if we have no locationList).
 */
modify MultiLoc
  CSGetOutermostRoom()
  {
    if(gActor) {
      local path = CSPathfinder.findPath(gActor, self);
      if(path && path.length())
        return path[path.length()];
    }
    return (locationList && locationList.length() ?  locationList[1] : nil);
  }
;

The issue is that the recursion stops at MultiLoc, it simply returns closest or first of the locationList. In the example above, gActor.CSGetOutermostRoom() will return Theatre, while jollyGreen.CSGetOutermostRoom() returns StageRight (say). Because they are different, this fails the canTalkToObj precondition and they can’t talk!

The fix is to restore recursion if the multiLoc’s location itself has a location.

/*  JJMcC - need to continue location recursion if Multiloc is not 'top level'
 */  
modify MultiLoc
  CSGetOutermostRoom()
  {
    if(gActor) {
      local path = CSPathfinder.findPath(gActor, self);
      if(path && path.length()) 
        // return path[path.length()];  // orig, non-recursive code
        // instead, continue recursion if .location exists
        //
        return (path[path.length()].location ?
            path[path.length()].location.CSGetOutermostRoom()
            : path[path.length()]);
    }
    // same for default case, replace orig code below
    // return (locationList && locationList.length() ?  locationList[1] : nil); 
    //
    if (!(locationList && locationList.length())) return nil;
    return (locationList[1].location ?
        locationList[1].location.CSGetOutermostRoom() : locationList[1]);
  }
;
1 Like

I didn’t use con space in my game, but I dig figuring this kind of stuff out.
An adv3 bug I never took the time to fix, because I was much newer to TADS when I had to resolve it: you can’t make a Container a MultiLoc without stack overflow. Probably a simple tweak of isIn(), but I never revisited it…