Create individual objects while running code


#1


Hey, this isn't related to a specific lesson, but I could use some help with a game I wanted to make.

I am making an rpg-style text-based game using javascript, and one of the items I wanted to make available for the player(s) to find is a magic rod. The total code is over 200 pages long, so I won't copy it to this post, but here is a general summary of how items work and I will put a sample below:

So far, I have each player set as an object with a property called 'inventory', which is an empty array. I have a function called 'dailyEvents', which randomly determines what happens on a given day in-game. One of the possible events is 'findItem', which is a function that will randomly choose an item from the array titled 'items' (which at this point contains only strings) and randomly choose a player. The item (string) chosen will be copied into the player's inventory array. When the player later chooses to activate the 'useItem' function, they type the name of the item and if it matches the string in their inventory, it starts a section of code that then applies the effect of the item in-game. Depending on the item, it then either is spliced from their inventory, or left there.

But the rods will have to work a bit differently because each rod will have a random number of times it can be used. To do this, I think I will need to make each rod an object with the property 'uses', which will be a number from 1 to 5, and the number will go down each time the rod is used. When 'uses' reaches 0, the rod will be spliced from the inventory. But since the items are randomly generated, I won't know how many rods I will need in advance, so I need the code to be able to generate unique objects (rods) after the code has started running.

As far as I can tell, I need to name each rod uniquely (rod1, rod2, ect) in order to make them unique objects, and I don't know any way to make the code do this on its own. Is this possible, or is there any other way to go about accomplishing this?

Here is a rough sample of how items are working in my code so far:


var player1 = {
    name: "Player One",
    maxEnergy: 10,
    energy: 3
};
player1.inventory=[];
var player2 = {
    name: "Player Two",
    maxEnergy: 10,
    energy: 7
};
player2.inventory=[];

var players=[player1, player2];
var items=["energy drink", "water"];

var game=true;
var day=0;
var a;
var b;
var c;

var dailyEvent=function() {
    a=Math.floor((Math.random()*2)+1);
    switch (a) {
        case 1:
            alert ("Nothing interesting happens today.");
            break;
        case 2:
            foundItem();
    }
};

var foundItem = function() {
    b=Math.floor((Math.random()*players.length));
    c=Math.floor((Math.random()*items.length));
    players[b].inventory.push(items[c]);
    alert (players[b].name+" found a "+items[c]+"!");
};

var useItem = function() {
    a=prompt("Who would like to use an item? ("+player1.name+" or "+player2.name+"?)");
    switch (a.toLowerCase()) {
        case "player one":
            a=player1;
            break;
        case "player two":
            a=player2;
            break;
        case "q":
            game=false;
            return 0;
    }
    b=prompt("Inventory:\n\n"+a.inventory+"\n\nWhat item would you like to use?");
    switch (b) {
        case "water":
            alert ("You have a refresihng drink of water!");
            for (i=0; i<a.inventory.length; i++) {
                if (a.inventory[i]==="water") {
                    a.inventory.splice(i,1);
                    i=a.inventory.length;
                }
            }
            break;
        case "energy drink":
            alert ("You feel your energy returning!");
            a.energy=a.maxEnergy;
            for (i=0; i<a.inventory.length; i++) {
                if (a.inventory[i]==="energy drink") {
                    a.inventory.splice(i,1);
                    i=a.inventory.length;
                }
            }
            break;
        case "q":
            game=false;
            return 0;
    }
};

alert ("Press 'q' at any time to quit.");

while (game) {
    day++;
    a=prompt ("This is day "+day+".");
    if (a==='q') {
        game=false;
    }
    if (game) {
        dailyEvent();
    }
    if (game) {
        a=prompt ("Does anyone want to use an item? (y/n)");
        if (a==="y") {
            useItem();
        }
        if (a==="q") {
            game=false;
        }
    }
}


#2

If the inventory is only supposed to contain strings, then putting another type of object might not be what you want? What about just adding the remaining uses to the name, and using it would rename it?

If you have two objects:

{name: 'rod', uses: 5}
{name: 'rod', uses: 5}

Then no, they are not unique, because there are two of them, but they are not the same, there are two of them, they are separate, changing one does not change the other. I don't see why you would need them to be unique, don't you just need to keep track of which is which? And if so, why would you mix them up, what problem do you need to solve?


#3

Yes, I would say that those are two unique items. Because if you have one in the inventory of player1 and the other in the inventory of player2, when player1 uses the rod in his/her inventory, that one then only has 4 uses while the other still has 5. This is what I mean by unique - they are not the same object.

Ok, let's say I add 'rod={uses:5}' to my array called items. Then I go down to the switch (b) and add a case for "rod" that loops through the player's inventory, finds the rod (because the rod is not a string that I can match up with the string "rod" entered by the player), and then alerts that the rod has been used and has 'a.inventory[i].uses' uses left. Then I add an if statement that splices the item from the inventory if the item has less than 1 use. Then I set 'i=a.inventory.length' to end the for loop (because I already found the item) and everything is set.

In case you want to test this yourself in the codecademy labs, copy my sample from the original post into the lab, then copy and paste:

rod={uses: 5},

to the beginning of the array called 'items', then copy and paste:

  case "rod":
        for (i=0; i<a.inventory.length; i++) {
            if (a.inventory[i]===rod) {
                a.inventory[i].uses--;
                alert (a.name+" has used a magic rod! The rod has "+a.inventory[i].uses+" uses left.");
                if (a.inventory[i].uses<1) {
                    a.inventory.splice(i,1);
                }
                i=a.inventory.length;
            }
        }
        break;

to the switch (b).

Two problems show up. The first problem is fixable. The rod doesn't alert itself as 'rod' because there is no string; it alerts itself as [object, Object]. Of course, that will require me to turn all of the items into objects and add a property called name, which is the string that I need. It means a tedious reform of my code, but if that's what it needs to work, I can certainly do it.

The bigger problem is that the rods are not unique. Say I play through this sample here until both players get a rod. So both players have [object, Object] in their inventory. I choose player1 and I pretend [object, Object] has a name and type 'rod', to activate that case that I added to switch (b). It plays through perfectly: it alerts that the rod has been used, and that it has 4 uses left. I do it again, and the rod has 3 uses left. But then if player2 uses his/her rod, it alerts that the rod has 2 uses left. Both player1's rod and player2's rod are treated as the same object. They are not unique objects. This is the problem that I can't solve.


#4

That's not what unique means. If two are identical, then they are not unique. Two things can be separate from each other without being unique.

Write a loop that does the transformation, are you the human or the computer? The human says what should be done, the computer carries out the repetitive tasks.
Or if you stay with strings then you can search for something that is either a full match or starts with a match and then continues with additional data

Treated as, or are the same? Why would you access the rod of the other player? Are they referring to the same object? If so why didn't you give each player their own?


#5

First of all, when you find a simple code which can transform random strings into a wide variety of objects and which takes less time to write than just making the objects, let me know. 'Repetitive' is not what I mean by tedious. I mean I have to sift through 200 pages of code and find the lines that need to be changed. You know, the reason editing code is always tedious. And due to my attempts to try to keep my code somewhat organized, I'm not just going to add random unnecessary loops everywhere to fix my mistakes, when I can just go in there and fix my mistakes. It's 200 pages when I don't waste space. The last thing I need to do is waste more space.

Did you not actually look at my code? If I could tell you why they were being treated as the same object, or why they were the same object, I probably wouldn't be here. And giving everyone a rod in the beginning would destroy the point of having a rod in there at all.

Ok, let me try to explain this situation again. I wanted to create an rpg. I had about 10 things that could happen in a day - nothing, encountering a monster, getting cursed, finding an item, etc. I made a random number generator to determine which of these things happened and, if applicable, to whom. Nine of them went more or less flawlessly. Then I moved on to finding an item. When the 'findItem' function is activated, it would choose a random number up to the number of items that were in the 'items' array - about 30. Since my plan was to make them all strings, I had the code copy that part of the array into a random player's inventory. Then, I gave the option for the players to use an item during battle and at the end of each day. If they chose to use an item, they would activate the 'useItem' function. This function would list off their inventory in a prompt (which worked fine, because they were all strings), and they would type the item that they wanted to use. The code would take that input, loop through their inventory, find which part of the inventory it was in, and then a switch statement matching the input with the various possible items would choose which bit of code to activate. If it was a 'mana potion', for example, it would set the player's mana equal to their maxMana, and then splice the 'mana potion' from their inventory. If it was a 'sword', then it would check that the player's hand slot was open, mark the player's hand slot as "sword", and raise the player's atk score. It would not splice the item because the item was still being carried. I would have another code elsewhere that would remove the attack bonus and open the hand slot if the sword left the inventory. Everything worked fine.

Then I needed to make a magic rod. Magic rods are not one-use like potions, but they are not permanent like swords. They can be used a certain number of times, and then they are no good. So I added "rod" to the items array, as a string, like everything else. Then I went to write what the rod does and realized that if I told the code to splice the item from the inventory, it would only be able to be used once, but if I didn't tell it to splice it, it would be used forever. I needed to find a way to keep track of how many times the rod has been used so that I could do an if statement to determine if it was time to get rid of the rod.

I decided that the best way to do this would be to attach the count to the rod itself, by making the rod an object with a property that kept track of how many times it could be used. So I made an item in the 'items' array that was "rod={uses:5}". Then that code would be copied into the inventory of whichever player was randomly given a rod, and each rod would have 5 uses. But that's not what happened - apparently I only created one rod in the 'items' array, and when it was added to an inventory, all that was really added was a link to the rod that is in the 'items' array. It didn't create a new rod to put into the inventory.

So I went to google trying to figure out how to make the code create its own objects after the game was running. Google had no idea what I was talking about. The world seemed to have no idea what I was talking about. So, I figured that maybe it wasn't possible - but I had to try. So I researched objects. I read more articles than I can count about how objects worked, and I couldn't understand more than the basics. None of them seemed to answer, or even address, my question. So then I spent a few hours staring hopelessly at my incomplete code. Needless to say, that also did not solve my problem.

So here I am, taking my last resort, and trying to get my question answered directly by asking about it directly. But I still don't seem to be able to explain what it is I want to know. So here I will try again: How do I make a code that will create individual/unique/separate/whatever-you-want-to-call-them objects while it is running, that did not exist before it started running? Yes, I can create an object using the format "var nameOfObject={propertyOfObject: data};". But I need the computer to do this for me. I can set up a template, but I need the computer to be able to make the object, possibly multiple separate objects with the same properties, on its own. I need a code that will create objects while running. I don't know how many other ways I can phrase this, so I just hope you understand this time. I don't care what format this comes in - I can rewrite the code around it if I have to. But I need to know how to do it.


#6

You can copy an object like this:

var bike = {wheels: 2};
var car = Object.assign({}, bike);
car.wheels = 4;
console.log(bike.wheels);  // 2, unaffected by the change to car
console.log(car.wheels);   // 4

You'll need to search by name rather than identity, because two separate objects are not the same, even if they are identical.

If you own a car and I copy your car, then they are not unique, but they are also not the same. If I crash mine, yours remains whole, that they are not unique does not prevent them from being independent.


#8

Ok, I'm actually still having problems. :x

So here is my test code that I was using to make sure everything would work well in my actual code:

var game=true;
var lighter={name:"\nlighter", uses: Math.floor((Math.random()*5)+1)};
var rod={name: "\nrod", uses: Math.floor((Math.random()*5)+1)};
var battery={name: "\nbattery", uses: Math.floor((Math.random()*5)+1)};

var items=[Object.assign({},rod), Object.assign({},lighter), Object.assign({},battery)];
var player={
    inventory:[]
};

var a;
var b;

while(game){
    a=Math.floor((Math.random()*5)+1);
    if (a===5) {
        player.inventory.push(items[Math.floor(Math.random()*items.length)]);
    }
    
    a=[];
    for (i=0; i<player.inventory.length; i++) {
        a.push(player.inventory[i].name);
    }
    b=prompt (a+"\n\nWhat do you want to use?");
    switch (b.toLowerCase()) {
        case "rod":
            for (i=0; i<player.inventory.length; i++) {
                if (player.inventory[i].name==="\nrod") {
                    player.inventory[i].uses--;
                    alert ("You used your rod! You have "+player.inventory[i].uses+" uses left.");
                    if (player.inventory[i].uses<1) {
                        player.inventory.splice(i,1);
                    }
                    i=player.inventory.length;
                }
            }
            break;
        case "lighter":
            for (i=0; i<player.inventory.length; i++) {
                if (player.inventory[i].name==="\nlighter") {
                    player.inventory[i].uses--;
                    alert ("You used your lighter! You have "+player.inventory[i].uses+" uses left.");
                    if (player.inventory[i].uses<1) {
                        player.inventory.splice(i,1);
                    }
                    i=player.inventory.length;
                }
            }
            break;
        case "battery":
            for (i=0; i<player.inventory.length; i++) {
                if (player.inventory[i].name==="\nbattery") {
                    player.inventory[i].uses--;
                    alert ("You used your battery! You have "+player.inventory[i].uses+" uses left.");
                    if (player.inventory[i].uses<1) {
                        player.inventory.splice(i,1);
                    }
                    i=player.inventory.length;
                }
            }
            break;
        case "q":
        case "quit":
            game=false;
    }
}

Ok, so the code runs. But when the player gets a rod, the rod has, say, 4 uses. I use the rod and it has 3. I use it again and it has 2, and it keeps going down like it's supposed to. So say I use all the uses and the rod's uses are now set to 0. The rod is spliced from the inventory. It all looks good, except that the next time I get a rod and use it, it tells me that the rod now has -1 uses. This rod is the same rod that was spliced from the inventory earlier. Why? What did I do wrong? How do I get it to give me a new rod?


#9

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.