What's the best way to give thangs custom attributes?

If you wanted a thang to have a custom attribute just for the purposes of one level (so not a permanent change to the thang), what’s the best way to do that? For instance, if you wanted to have a group of soldiers, and they would all have an individual “age” integer attribute, or a “favColor” string attribute, what’s the most straightforward way to introduce that for a level?

You could make them Programmable and add that in their chooseAction() method or something like this, then set the Programmable method such that no team can write or read it. Custom logic on a per-level basis with no additional Component needed. Can you explain more about what the age or favColor will be used for?

Well, I was brainstorming some ways to give practice with various math functions, and as it is there’s only so many things you can do with health and position, so I thought it might open up more possibilities if you could introduce other numbers into the mix. Perhaps something like having all the soldiers share the same code (which I figured out how to do by looking at the Hunter Triplets level as an example), and saying to send each soldier to the position where the x coordinate is the square root of their age and the y coordinate is something involving the pow() function. I can probably come up with something more interesting than that, but you get the idea.

It would also be interesting if random values could be assigned this way so that the level would play out differently every time (so you can’t send the soldiers by name, you have to do the math), but that would require slightly fancier goal capabilities.

Good ideas! I’ve extended programming.HasAPI to let you add in whatever additional properties you want to a Thang. You’ll also have to include the ones you want to be able to programmatically access in the HasAPI Component’s apiProperties and perhaps the Programmable Component’s programmableProperties.

Give it a shot and let me know how it goes.

OK, adding a new property to the API seems easy enough. However, two questions come up:

  1. How do I set an initial value to my new property?

  2. Is there a way to get the property value to not be displayed in a tooltip when the user hovers the mouse cursor over it when they’re editing code?

As a follow-up, a set-up that sort of works:

  1. Add property to HasAPI component
  2. In Programable component, add method that sets initial value of this property and set the permissions to not readable
  3. Call this setter method first thing in chooseAction()

Feels a bit clumsy, but it does get a value in there, and also prevents it’s value from being visible when you mouse over. I still feel like there’s a better way to do this though.

I meant that you can add the new properties like this:

I haven’t made a better way to exclude some variables from hover tooltips, no. Why don’t you want the variable to be available for hover debugger?

Well, my fear is that if the value is visible, then the temptation rises to solve the problem in their head and just hardcode the solution (directing units by name based on who has the right values for instance).

That defeats the purpose of trying to teach yourself to code, so perhaps I shouldn’t worry about it, but especially if people ever plan to use this for graded homework assignments I feel like we should guard against the more blatant ways of gaming a level to get the victory screen without actually learning the concept at hand.

OK, let me just put it out there for some feedback. Both of these levels aren’t quite finished, but I have the basics in place. I’ve handled the extra property differently in each. Which is the more appropriate way?

Level: Mod Squad http://codecombat.com/play/level/mod-squad

In this one I have the “favNumber” property added to the apiProperties group so it doesn’t get a default value. Then the user is forced to call this.setFavNumber() as their first line of code. With the right permissions, this keeps the user from ever seeing the value of any soldier’s favNumber property, so they need to code things up the right way.

Level: Mod Squad: Supplies Party http://codecombat.com/play/level/mod-squad-supplies-party

In this one I have the “wages” property added as it’s own thing like you show above, meaning that it has a default value for each soldier. This means they don’t need to include a call to a method that sets the value, but it also means that they can mouse-over “this.wages” to see the value. This would then allow them to solve the problem in their head and then write code like

if(this.name == “Richard”) {
this.pickMushroom();
}

Which would bypass the whole point of the level, which is to get them to use the modulo operator.

I’d prefer to handle extra properties the second way, but I don’t know how much to worry about being able to hide values from the user.

=================================

As a side note, in the second level I have 4 “Get to Location” goals, but only the first three seem to matter. Annie can just sit there and the level still succeeds. Any idea what’s going wrong there?

The second way is the right way to do it. There won’t be a way to let player code access wages without letting them see the value (that would kind of go against the point of letting players see the data). The wages are high enough that it’s easier for the player to figure out the modulo operator than to solve four instances of the problem.

Also, we haven’t enabled it by default for every level yet, but with API protection on, a player won’t be able to reference a property that is not in apiProperties or programmableProperties, so they wouldn’t be able to pend on the Thang’s names. At some point soon I hope to get the last bugs out and enable it for all levels, and fix up the levels that don’t have the right programmableProperties to work with it. You should also add wages the programmableProperties so that the player knows how to reference it.

I’m not sure why the Get All to Locations goals are firing wrongly. I’d have to dig into it with some logging to see what’s going on. Care to open a GitHub issue for it?

@nick

Well, I was trying to hunt down the source of the Get All to Locations issue, and within the space of an hour it went from not working properly to working properly. I don’t see any code commits during that time frame, so I’m really stumped as to why it was broken and now it’s working.

Nope, spoke too soon. Figured out how to break it again.

Basically, if I have a “how many” clause for the “get all to locations” goal, it works, and if I don’t (which should require all sub-goals) it doesn’t work.

@nick @Darredevil

Nick and Darredevil, since the two of you wrote the InitGoalState and GetAllToLocation functionality in GoalManager, would you mind weighing in on the proposed change here:

I believe this will fix the problem with compound goals like GetAllToLocation needing a HowMany to work properly. Basically, since every call to InitGoalState creates a fresh state[ProgressObjectName] array, only the last subgoal was actually surviving. I believe that it should be safe and correct to reuse a state[ProgressObjectName]. Am I missing any potential side effects?

I’ll check it out tomorrow–I actually have a hard time keeping all the goal manager stuff straight, especially the GetAllToLocations and HowMany stuff (as I guess you have seen by finding all my bugs in it–nice work!).