Making a Smart Vendor Development Malkin | 11/11/2013 | 3 | A variation on the CDN "Making a Vendor for C3" tutorial with extra features - being a smart vendor and vending food on a timed basis.
This is a variation on the Creatures Development Network "Making a Vendor for C3" Tutorial, incorporating some extra features, like a hit and timer script for the vendor. Note that this does not cover the creation of the sprite file or the PRAY file, which are both essential to making sure your agent works properly.
Part of developing an agent is working out its 'story' - how it will behave in your world under a variety of circumstances. When you make an agent, you do not travel with it into other people's worlds, telling it every step of the way what to do, it has to have its own instructions. Writing down exactly what you want it to do is a good first step.
The story:
We want to make a vendor using the 'fishbowl' sprite that injects in the C3 corridor (or the bottom of the DS meso) that can be pushed and pulled by creatures to make food, that can be hit so that grendels can relieve their angst, and the vendor will occasionally vend on a timed basis, so that creatures can get some food and hopefully notice the vendor so that they can get more food.
The story of the vendor's food is that it uses the cheese sprite from the infinite cheese machine, it appears from the vendor when it's pushed, or pulled, or on a timed basis, it makes a noise when dropped and it stimulates a creature when it's eaten and then vanishes.
Potential problems to solve are:
We don't want piles of food, so we're going to make sure that if there's a lot of our food nearby, the vendor won't make more food. (This is often called being a 'smart vendor'.)
For learning's sake, we're going to make sure that the vendor and the food stimulate the creatures properly.
For good practice, we should make sure that any stray food items will eventually vanish - but not before they can be eaten!
We need to make sure that we're using the right images in our sprite file, otherwise the agent will appear wrongly in-game and may produce errors when we try to change its pose.
This story is missing a few of the finer details, but we need to tell the agent everything it needs to work on its own.
Make the vendor
When making a new item, you need to do it in one tick, (INSTantaneously) so that all of its attributes get installed properly.
Create new simple object with the classifier 2 23 10, using the sprite 'fishbowl'. Use 2 pictures from inside the 'fishbowl' sprite, and use those 2 pictures starting at position 0 in the file. If you take a look with Sprite Builder or Monk, you'll notice that pictures in the sprite file are counted starting with 0.
new: simp 2 23 10 "fishbowl" 2 0 5000 |
Give it attributes that allow it to be picked up and activated by the hand. Responds to gravity and boundaries.
Lets creatures push, pull and hit the vendor.
A permeability that will allow it to sit on wooden platforms (rather than fall through them)
Will not bounce.
Will not slide around, at all.
It's moderately heavy.
Use doif to test if you can move into the preferred location. (The corridor area of the ship).
If the move works, use mvto to inject yourself to the preferred location.
If the move doesn't work
Use mvsf to inject yourself to that location. While mvto gives pixel-perfect locations, if they're invalid, it can cause errors with the object. Mvsf gives a valid location near the coordinates specified.
If mvsf doesn't work (e.g. because you're injecting this in DS standalone)
Use mvsf to inject this to a random location in the bottom of the DS meso.
End the move-testing doif
Set the timer running. This command tells the timer how often to fire – at the moment it's set for 3600 ticks, which means the vendor will try to vend a new item spontaneously every 3 minutes. It's easy to set up a spreadsheet in Excel or similar products to help you calculate ticks. The rule of thumb is that there are 20 ticks per second. As there are 180 seconds in 3 minutes, I've multiplied 20 by 180 to give me this number.
Move the camera to look at the object – this is just a cute feature, but can be useful for the player because we've done the 'C3, then DS' install.
Now that we've put it in the world, we're going to tell it what to do when it's pushed.
Vendor Push
Firstly, for learning's sake, we're going to stimulate the creature that activated the vendor. Putting it as the very first thing ensures that it always stimulates the creatures, no matter if the vendor vends or not. Consistency is important for learning, so we always want them to get a feeling from pushing vendors.
And now we're going to lock the script so that the rest of it doesn't get disturbed.
Only vend if there aren't already many nearby. Set a field of view of 300 pixels. The larger your range, the further away your vendor can search for vended items. Experiment with this number to find a happy medium.
Make a temporary variable, va66, equal to 0.
This block will allow us to see how many agents with the classifier 2 11 1000 are in that 300 pixel range.
Firstly, in a loop, we see one example of 2 11 1000:
If the TARG we found isn't NULL (preventing a common caos error)
Add 1 to our temporary variable for that particular item we found.
Closing the 'if the TARG isn't NULL' error check.
Move on to the next part, when we're done with our esee loop. Like every doif has an endi, every esee has a next to close.
ESEE leaves the target at the last ‘seen’ agent (the last food item that we count), so we need to manually change the target back to the vendor by adding in TARG OWNR. If we didn’t, when the food decayed, the vendor wouldn’t vend!
Now that we have this temporary variable, we can check it, and if there are four or fewer food items nearby, then that's a small enough number to make some more food. Again, experiment with this number – it's all about finding a happy medium.
Make the bowl animate
anim [0 1 0 1 0 1 0 1 0 1 0] |
Get the coordinates for the bowl – set a temporary variable (va00) as the leftmost part of the vendor. Once you've got that temporary variable, add 30 to it to bring the location closer to the middle. This is our X-location.
setv va00 posl
addv va00 30 |
Get the coordinates for the bowl – set a temporary variable (va01) as the topmost part of the vendor. Once you've got that temporary variable, add 10 to it to bring the location slightly down from the top. This is our Y-location. Moe explains this really well in his Beginner's Tutorial Part 3.
setv va01 post
addv va01 10 |
Now that we've set our location for the item to spawn from, we can produce the item! As we're going to be re-using this code in a few places, it has to be the same every time. Like making the vendor, we have to tell the game that this agent has to be made instantaneously:
Food attributes:
new: simp 2 11 1000 "infinite_cheese_machine" 2 44 4900
attr 195
bhvr 48
elas 30
fric 50
accg 3 |
A similar test move and mvsf failsafe, this time using the temporary variables we set just earlier as our X and Y locations.
doif tmvt va00 va01 = 1
mvto va00 va01
else
mvsf va00 va01
endi |
Give it some velocity!
velo rand -10 10 rand -15 -20 |
Give it a tick. This starts the timer script for the food. As we're going to use the timer script to destroy the food, we will give it a longish timer. The relationship between the tick of the food and the tick of the vendor will determine whether your vendor tends to have a surplus of food at its base, or whether its automatically created food items are few and far between. I'm giving it a random tick between 5 and 10 minutes long. Hopefully that will be long enough to let creatures find it. Again, this is a matter of experimentation. Because this tick is longer than the vendor's timer (3 minutes), the vendor will usually have a small pile of food at its base.
Make it smell like food. This will let creatures navigate to your foodstuff even when they are far away.
Now that we've finished making the food, we need to go back to the start of our code and end the population count doif.
Now we need to formally end the push script.
Vendor Pull
We want the pull script to work in the same way as the push script, so we need to change the part at the beginning when it wants to know what script it is – push scripts are scrp 1, pull scripts are scrp 2.
Firstly, for learning's sake, we're going to stimulate the creature that activated the vendor. Putting it as the very first thing ensures that it always stimulates the creatures, no matter if the vendor vends or not. Consistency is important for learning, so we always want them to get a feeling from pulling vendors. Keeping this line in is important for what we're going to do next.
To prevent copy-paste errors, we can now simply tell the pull script to run the push script belonging to the owner of this script (ie. the vendor):
Now we need to formally end the pull script.
To make our vendor act like the C3 vendors, and feel like a C3 vendor for the creatures, we need to add a hit script that stimulates – I've decided not to make the hit script vend an item because we vend items so many other ways with this vendor, but it could vend an item with the hit script if you want.
Vendor Hit Script
Make a noise – this noise is C3-exclusive, so you might want to choose something else.
Jump a bit.
Stimulate with 'I have hit a machine' - your grendels will love hitting this vendor!
Finish the hit script.
Now we're going to make our vendor vend food 'just because', on a timed basis, using the tick of 3 minutes that we set way back when we first injected the vendor.
Vendor Timer Script
This is to solve a potential problem, what if you're carrying the vendor from one place to another? So we put this in to stop the vendor from vending by itself if you're holding it. (This will also prevent it from automatically vending if you put it in a lift!)
Run the push script (to vend the object).
End the 'am I being carried?' doif.
End the timer script!
Now we're pretty much finished with the vendor's story, so we have to make sure that the food knows exactly what it needs to do. It already knows when it can be created, so now we have to make sure it behaves itself when it's in the world.
For the timer script – this will fire only once, and we only want it to remove the food item, so it only has to be very simple. The script locks, and then it kills the ownr of the script.
*Food timer script
scrp 2 11 1000 9
lock
kill ownr
endm |
Now for the important stuff - making sure the food is edible!
*****Food eat script
scrp 2 11 1000 12
lock
sndc "chwp"
stim writ from 79 1
pose 1
wait 20
kill ownr
endm |
Makes the cheese make a noise when it falls down.
** Cheese Collision Script
scrp 2 11 1000 6
doif wall = down
snde "dr10"
endi
endm
|
Now that we've told the agents what to do when they're in the world, we have to tell them how to say 'goodbye' properly when they're removed.
*Remove script
rscr
*count all the vendors and kill them
enum 2 23 10
kill targ
next
*count all the food and kill them
enum 2 11 1000
kill targ
next
*remove all scripts from the world.
scrx 2 23 10 1
scrx 2 23 10 2
scrx 2 23 10 3
scrx 2 23 10 9
scrx 2 11 1000 9
scrx 2 11 1000 12
scrx 2 11 1000 6 |
Key Issues:
One of the key points with making one object create another object is Location, Location, Location. You need to get coordinates for the new object to appear, which is normally based on the position of the vendor object, so that it's easier for the player to see the correlation. This is covered in our object above by the code
setv va00 posl
addv va00 30
setv va01 post
addv va01 10 |
The other point when choosing a location is that you need to check that the new object can fit where you want to put it. If the vendor is placed somewhere very small (and the created object is very big) the coordinates for the new agent may not be a valid location and produce an error.
This is covered in our object above by the code:
doif tmvt va00 va01 = 1
mvto va00 va01
else
mvsf va00 va01
endi |
You'll notice the use of MESG WRIT to create what's called DRY code - Don't Repeat Yourself. In this way, although food is vended in three different ways by this vendor (push, pull and timer script), the push script holds the key information once, and the pull and timer scripts re-run the push script.
Further Reading:
*Beginner Tutorial Part 1, Beginner Tutorial Part 2, Beginner Tutorial Part 3, Chocolatey Addendum by Moe.
*Balloon Maker Tutorial at the CDN.
*Basic Plant Script by Zareb and Liam.
*Mac Agenteering by SG - covers a bubble wand toy which makes bubbles.
Moving On:
Once you're confident on how to make one object produce another object, the possibilities are limitless - while vendors can produce anything that is desired - toys, devices, eggs; the concept is used in many other places in C3 and DS. Plants and seeds are a good example of this - the plant makes a seed, which, if it is not eaten, makes a plant. A lot of critters make critter eggs, which then in turn make more critters.
Another script that could be added is an ear script, to create a vendor that makes food in response to creatures complaining about hunger.
Randomisation could also be used to create more variety in the look of the foodstuffs that are vended.
Updated by Malkin on 11/11/2013 - + Further Reading. Updated by Malkin on 4/21/2014 - updating with targ ownr information. Updated by Malkin on 11/12/2014 - updating Creatures Wiki links. Updated by Malkin on 4/28/2022 - updating a Guide to Mac Agenteering. |