General Code Help

#1

I’m going to make another general code help thread so I don’t end up with a bunch of topics. I’ll just drop questions in here for awhile. I’m running into issues with append on a list. I have a list called flags = [] on a Person Actor. I’m trying to append to it when I enter the room, but it’s not appending. I’ve set break points, but never get the value.

if(heroMom.flags.indexOf('first intro') == nil)
        {
            clearScreen();
            "<q>There you are, <<me.name>>.</q> <<me.name>><<me.charPossessive>> mother says, her eyes catching his. \n\b";
            inputManager.pauseForMore(true);
            "She paces a few steps.\n\b";
             inputManager.pauseForMore(true);
            "<q>How could you sleep so long?</q> She asks. <q>Adryx's retirement is this evening.</q>\n\b";
            inputManager.pauseForMore(true);
            "<q>Oh no...</q> <<me.name>> says. <q>I almost forgot.</q>\n\b";
            inputManager.pauseForMore(true);
            "<q>You did forget, silly.</q> She sighs, her worried look washes away. <q>No worries, I have plenty of time to finish 
            the flowers. We have a beautiful group of girls to pass out leis.</q> Her brow sharpens
             <q>Can you get me some Lyrsa flowers from the forest?</q>\n\b";
            inputManager.pauseForMore(true);
            "<q>I only need a few, and then Sarah wants to see you. She left a note.</q> She cracks into a smile, <q>She's such a     cute friend. 
            But the flowers first, okay?</q>\n\b";
            inputManager.pauseForMore(true);
            "<q>Alright.</q> <<me.name>> replies.\n\b";
            inputManager.pauseForMore(true);
            clearScreen();
            womanSweeping.location = [heroMom];
            heroMom.curState = womanSweeping;
            heroMom.flags.append('first intro'); // Append here is not working
        }
      }
#2

List is immutable data structure, please read here for explanation: [url]https://intfiction.org/t/frequency-and-content/111/1]

#3

Genius, didn’t know I was working with a return value. Thanks.

#4

What is procedural code for removing a game object from the world?

#5

Were you just looking for:

object.moveInto(nil)

or was it something more complicated?

#6

Objects defined at compile time (which is what you’ll be using 99.9999% of the time) cannot be deleted. You just move them to a location where they cannot ever be seen. That location can actually just be nil, as Otto mentioned, which indeed is the most common way of removing an object from the game world.

If you had dynamically allocated objects (rare, but sometimes useful) that you want to delete, you first move them to nil and then just delete all references to them, so that the garbage collector deletes them (same thing as C# or Java.)

#7

Perfect. Thanks. I’m facing a problem with inventory. I have an inventory list on my main character, and append classes such as weapon, armor, and item. This works, but it makes things complicated. I have to override inventory to output from the list, but also have to override actions altogether it seems. To equip an item I would have to capture raw input and run a capture group regex on it. I could go back to the normal Tads 3 way of using objects and inventory but having 10 sticks or 10 potions gets hard with disambiguation I think. I don’t mind the capture groups but it’s not clear in the documentation how to do this. In .net c# you get something like an associative array back with the data.

I’ve started in the Regex direction, with a verb defined but that doesn’t work. It’s expecting the item to be in the normal Tads inventory, understandable. Can I use a single verb rule with no objects? Can I return the input inside execAction()? Then I could parse with regex and get the capture group. Something like this:

DefineIAction(Equip)
     execAction() {

            // capture input here and store to string variable
            // if just 'equip' or 'e' output error
           // if match to something like 'equip x' or 'equip the x' start processing

    }
   ;

   VerbRule(Equip)
     'equip' | 'e'
     : EquipAction
     verbPhrase = 'equip/equipping'
   ;

Either way there is a sacrifice. If I go back to the tads inventory I’m going to have to limit the player to picking up one of each item. That will make coding easier, but there’s a loss in game play.

#8

This question is not clear enough to me, so I’ll comment only some parts. Regular expressions are much like in any other language. For example this rexSearch will match contents of title tag which is saved into first capturing group. You can use rexGroup call to read this group like this:

if (rexSearch('<nocase>[<]title[>](.*)[<]/title[>]', title)) title = rexGroup(1)[3];

The number 1 is the number of the group and return value is a list, third element is the match text itself (first is an offset and second is a length of the match).

Anyway, limiting the player to picking up one of each item shouldn’t be hard, but parsing raw input by yourself on the other hand will.

#9

Thanks for the reply on regex. I’ll try to explain. Using tads inventory out of the box causes problems right off the bat for jrpg type games. Say if you buy 10 potions. When you run action drink potion it will say which one: the potion, the potion, the potion, the potion. With endless disambig problems.

On the bright side of the hard way there are less commands in jrpg: use, wield, remove, attack, cast.

Here’s how I modified thing:


// modify thing

modify Thing
 quantity = 0
 value = 0
 classification = ''
 
    dobjFor(Take)
    {
        
        verify()
        {
            
            local inventory = me.inventory;
                       
            if(inventory.length() >= me.inventoryLimit)
            {
            
               // illogical inventory full.
                illogicalAlready('<<me.name>> has a full inventory.'); 
                
            }
            
        }
        
        action()
        {
             
             local inventory = me.inventory;
             local itemFound = nil;
                // test if holding an item of that type
                foreach(local x in inventory)
                {   
                     // if holding an item of that type increment amount
                    if(x.name == self.name)
                    {
                        
                        x.quantity = x.quantity + 1;
                        itemFound = true;
                    }
                    
                }
                
                if(!itemFound)
                {
                    
                     // if not holding an item of that type add the item with 1 incremented to it
                    self.quantity = self.quantity + 1;
                    me.inventory = me.inventory.append(self);
                    "<<me.name>> picks up the {the dobj/him}.";
                                  
                }else{
                    
                    me.inventory = inventory; 
                }
               
                 self.moveInto(limbo);   
          
        }
     // end take   
    }
   
;

I don’t think dealing with the raw parsing would be too bad if it was done inside each action. The follow code is really rough. I haven’t finished re-writing it from where I cut into place from. I would have to do the regex stuff in there.

[code]DefineIAction(Equip)
execAction(){

        local inventory = me.inventory;
        local found = nil;
        // test if carried
        
        local input = // capture user last input here
        
        verify()
        {
            foreach(local x in inventory)
            {
               
                if(x.name == )
                {
                    
                    found = true;
                    
                     // test if one already equipped
                    foreach(local x in inventory)
                    {
                    
                    
                        switch(x.classification)
                        {
                            
                            case 'wpn':
                                if(x.name ==  && x.isEquipped == true)
                                {
                                    // illogical already have one equipped
                                     
                                }
                                break;
                            case 'arm':
                                if(x.name ==  && x.isEquipped == true)
                                {
                                    // illogical already have one equipped
                                     
                                }
                                break;
                            default:
                                    // illogical item can't be equipped
                                break;
                                
                        }
        
                    }
                
                }
                    
                // end loop through inventory
            }

            if(!found)
            {
                // illogical not holding item
                
            }
        
        // end verify
        }
    
        action()
        {
            
        }
}

;

VerbRule(Equip)
(‘wield’ | ‘equip’)
: EquipAction
verbPhrase = ‘equip/equipping’

;[/code]

I guess the only thing I need to know is how to get the last input to the command line. I looked everywhere. InputManager, action, various different objects, and Google, lol.

#10

When I get to ‘use’ there is an issue. Inventory is going to be in a list. Without using Tads inventory I can’t easily create verbs. Using no objects the verb would literally have to be ‘use’ same as ‘inventory’. And I can’t use TAction because it will look for the item automatically, or can I override that?

Edit:

I think I found a way to use the original Tads inventory after all. I can just add a quantity variable to thing. When if I pick up an item while already holding that type, I can increment the quantity. Then I could just use the inventory as is out of the box. Only minor tweeks would be needed here and there, as apposed to overhauling the entire system, lol. The only thing I need to research is returning the characters current inventory list, looping over it, doing typeof tests, ect.

#11

Okay, I finally decided to go with tads inventory, too easy to do, and my line of thinking was just off. I’d like to work on battle logic for a bit, but a daemon is giving me troubles. I have a one set up in construct to get the monster going. I have Actor modified with logic, and a static object created with logic. It’s a bit confusing. Also the first argument for a daemon is no well documented at all. I’m hoping to get the daemon working like a 30 second timer.

I believe it’s the constructor. It’s on the static object and the class. Not sure if that’s part of the issue or not.

Actor

modify Actor
    
   roll(num1, num2)

    {    /* find out which of the given numbers is larger */
        local maximum = max(num1, num2);
        local minimum = min(num1, num2);
        
        /* calculate the random number */
        local tempMax = maximum - minimum + 1;
        local randNum = rand(tempMax) + minimum;
        return randNum;
    }
    
;

Static Monster

// monster
monster: Monster
    lowerDmg = 1
    upperDmg = 4
    mp = 0
    isFlying = nil
    canFly = nil
    hasBurrowed = nil
    canBurrow = nil
    spells = []
    rewards = []
    upperGold = 2
    lowerGold = 8
    lowerXP = 4
    upperXP = 8
    atkCount = 0
    atkTargetCount = 0
    target = nil
    actionDaemon = nil
    location = combatRoom
    
    construct()
    {
        "Construct called";
         actionDaemon = new Daemon(self, &actionProcess, 1); 
        
    }
    // has roll(l, u) inherited (return random number between range)
;

Monster Class

class Monster : Actor
    lowerDmg = 0
    upperDmg = 0
    mp = 0
    isFlying = nil
    canFly = nil
    hasBurrowed = nil
    canBurrow = nil
    hasDoubleAttack = nil
    doubleAttackChance = nil
    spells = []
    rewards = []
    upperGold = 0
    lowerGold = 0
    lowerXP = 0
    upperXP = 0
    atkCount = 0
    atkTargetCount = 0
    target = nil
    targetChance = 65
    actionDaemon = nil
    
    
        // use constructor to set actionDaemon event 
    construct()
    {
        "Construct called";
         actionDaemon = new Daemon(self, &actionProcess, 1); 
        
    }
    
        // daemon function for ticking attacks
     actionProcess()
    {
            "working";
            local turnComplete = nil;
            
           if(self.target == nil)
            {
                
                local roll = roll(1, 100);     
                if(roll <= self.targetChance)
                {
                       // target set
                    self.target = me;
                    
                }else{
                    
                    // failed to set a target
                        // no target set
                    
                }
            }else{
                // target already set
                
            
                // end target already set
            }
        
    }
    
;
#12

I believe construct() is called only on dynamically constructed objects with new operator, but you can add your initialization in initializeThing() method.

Daemon is not realtime so the last parameter is number of turns after which it will fire and not seconds. You can use RealtimeDaemon if you want 30 seconds. First and second parameter of the Daemon are for passing a callback - first parameter is simply the object on which the callback method is defined and second is the name of the callback method so self and &actionProcess are correct.

#13

Perfect, understand 100%. I’m looking through my old game too. Wow, was I way off in coding. This is going much better. Thanks for the help to, I appreciate it. I’m learning.

#14

Is there a general way to return a list of actors in the room?

#15

You can use something like this:

    getNpcsIn(location)
    {
        local npclist = [];
        if (location == nil) {
            return npclist;
        }
        for (local npc = firstObj(Person); npc != nil; npc = nextObj(npc, Person)) {
            if (npc != libGlobal.playerChar && npc.isIn(location)) {
                npclist += npc;
            }
        }
        return npclist;
    }
#16

Ah, thanks. I finally got something rigged up for that. Not as good, but does the trick. I made a monster from my class Monster but it’s not showing up in the room. I had this problem before with classes, but can’t remember the fix at all. The initializer is called, but the item is not in room listing and I can’t look at it. I think last time vocab words were the issue, but I don’t know. I was wrestling with initializeWithVocabWords but Tomas mentioned a problem with that.


combatRoom: Room 'Arena'
    "The arena is for fighting a creature in"
;

monster: Monster        
    name = 'Lizark'
    desc = 'A nasty looking lizark.'
    vocabWords = 'lizark'
    location = combatRoom
    lowerDmg = 1
    upperDmg = 6
    mp = 0
    canFly = nil
    isFlying = nil
    canBurrow = nil
    hasBurrowed = nil
    canClimb = nil
    isClimbing = nil
    canSwim = nil
    hasSwam = nil
    hasDoubleAttack = nil
    doubleAttackChance = nil
    willTargetMonsters = nil
    spells = []
    attacks = []
    specials = [] // a list of specials by string 'fly' 'swim' 'burrow' 'climb' 
    rewards = []
    upperGold = 0
    lowerGold = 0
    lowerXP = 0
    upperXP = 0
    atkCounter = 0
    atkCount = 0
    target = nil
    targetChance = 65
    targetActorChance = 95
    changeTargetChance = 20
    spellChance = 0
    specialChance = 0
    specialEndChance = 25
    isAgressive = nil
    actionDaemon = nil
    
    initializeThing() 
    {
         initializeVocab();
         actionDaemon = new RealTimeDaemon(self, &actionProcess, 30); 
        "Initialized Lizark/n/n";
    }
    // has roll(l, u) inherited (return random number between range)
    
    // has actionProcess form Monster class
;
#17

The exceprtion from your source code is incomplete, so it cannot be tested, but first what came to my mind is that you should call inherited whenever you override any method. Let’s have a look on initializeThing() method on the Thing class:

[code] initializeThing()
{
/* initialize our location settings */
initializeLocation();

    /* 
     *   if we're marked 'equivalent', it means we're interchangeable
     *   in parser input with other objects with the same name; set up
     *   our equivalence group listing if so 
     */
    if (isEquivalent)
        initializeEquivalent();

    /* if we have a global parameter name, add it to the global table */
    if (globalParamName != nil)
        langMessageBuilder.nameTable_[globalParamName] = self;
}[/code]

You are calling only initializeVocab(); and then your own code, but you’ve completely suppressed standard behaviour, initializeLocation() being the foremost. So each time you override any library method, call inherited(); and then do your own business unless you know you want to suppress library intentionally.

#18

Hold on, I didn’t follow that easily. What am I calling that is suppressing something and what is being suppressed? I guess you mean calling initializeThing overrides the entire built in initialize process? I’ve updated the code. The creature is in the room, but I don’t get desc when I look at it. Says: nothing obvious is happening.

This is a hard line.

 langMessageBuilder.nameTable_[globalParamName] = self;
#19

When you override a method, you are replacing the existing code of that method with new code. The previous code is not executed anymore when the method gets called.

Usually when you override a method, what you want is add additional code to that method, not replace the existing code. This is true for all methods you override, not just initializeThing().

When you override a method (either using the “modify” keyword on an existing class or object, or by redefining the method in a class or object that inherits from another,) unless you have good reason not do, you should always make sure to call the overridden code. This is done using the “inherited” keyword, which serves as the name of the overridden method.

In this case, you would do:

initializeThing()
{
    inherited();

    // your own code here.
    // ...
}

If the method you are overriding takes arguments, you’d pass them on to inherited(). Like “inherited(arg1, arg2);”

Sometimes you have reason no to call inherited. For example when you explicitly want to replace rather than extend an existing method’s code. Sometimes you have reason to call inherited() last, at the end of your code, rather than as the first thing as in the above example. Sometimes you have reason to conditionally suppress a call to inherited().

But the point is: when you override a method, the previous code of that method will NOT be executed anymore. It’s gone. In this case, your Monster objects won’t be initialized correctly.

This is a very common pattern of object oriented design. You’d need to do the exact same thing if you were working with a class hierarchy in C++, Java or C#. I believe you mentioned elsewhere you’re not new to programming, so hopefully this whole thing rings a bell.

#20

Yes, but ‘not new to’ and ‘good at’ are not synonymous. I did learn something here though. So If I just call inherited in initializeThing it will run all the normal code inherited from the original function?

I have an object question.

Will obj == obj determine if it’s the exact same object or do I have to capture a uniqueID?