Having finished my improved version of the bees and hives update, as well as my improved beelacanth, I'd like to go into detail about the changes I've made to the code, which means lots of rainbow-highlighted CAOS scripts. If anybody reading this post happens to be colorblind, I apologize in advance. To the anonymous commenter who wanted more CAOS code: you're welcome.
Better Bees and Hives
Much of the code is actually unchanged, except that references to the classifier for bees have been changed to reflect their new classification as pests. The push and pull scripts for the hive have been swapped, with the following change made to the push script.
Original Pull | New Push |
---|---|
scrp 2 8 1 2 stim writ from <...> snde hony setv actv 0 endm | scrp 2 8 1 1 doif from eq pntr mesg writ ownr 1 else stim writ from <...> snde hony setv actv 0 endi endm |
The additional lines essentially preserve the original functionality of clicking on the hive to produce bees. If the push comes from the hand, it calls the new pull script (which is the original push script). Otherwise, it proceeds with the new push script (which is the original pull script).
The code for the bees, including the monstrous enter scope script I annotated previously, has been left entirely unchanged except to modify the classifier number for the bees. The problem wasn't actually in the bees' behavior but in their inconsistency with other, friendlier critters, and I solved this by creating a new "pest" genus.
Download Better Bees and Hives
Updated Cave Fly
I don't personally use the cave flies, but I figured that if I was creating a new genus for bad critters, I should update the cave flies to occupy the new category alongside the bees. Also, for some reason, the official cave fly COB also includes updates to the jukebox and pianola. I understand the connection here - all of the objects in the COB use or are being updated to use new chemicals that are found in the PMN genome. But in my opinion these should have been split into separate injectables so you could update the music without the cave flies and vice versa. So I did just that.
Updated Shee Seed Launcher
Now this is the meat of this post! There was a lot wrong with the original beelacanth. The seed launcher was classed as a vendor, contributing to the general inconsistency problem in that class. I wasn't really sure what to do about this at first, since making it feed creatures directly didn't feel right, but ultimately I decided to reclassify the launcher as a toy and give it boredom-- and NFP-- like most toys. Otherwise, its scripts are unchanged.
The beelacanth itself, however, was heavily reworked.
First of all, code is a lot easier to read when you know what the variables represent; that's why in modern programming we use variables with descriptive names like NumCreaturesInWorld. But in ye olden times we had to make due with "obv0" and the like. So to make your life easier, here's a reference:
- obv0 is the seed timer. Once a seed is grown on the plant, this variable starts counting. When it reaches a certain level, it triggers the plant to eject the seed.
- obv1 is the growback counter. Every time the flower grows back after the seed is picked, this counts up.
- obv2 is the status of the plant: 0 is a new plant, 1 is a plant that is growing/unpollinated, 2 is a plant that has been pollinated, and 3 is a plant that is dying.
Note: For space, I've line-wrapped certain lines with a ... indicating that a line is a continuation of the previous line. The ... is not in the actual script. Also, as usual, this code has been indented for readability.
Original Plant Push | New Plant Push |
---|---|
scrp 2 4 14 1 doif obv2 eq 2 pose 8 setv var0 posl setv var1 post doif obv1 ge 2 rndv var2 0 1 doif var2 eq 0 setv obv2 3 else setv obv2 1 endi endi gsub frut snde chwp stim writ from 10 255 0 0 …57 30 20 50 67 5 0 0 kill ownr endi subr frut inst new: simp flwr 1 10 500 0 setv clas 33949952 setv attr 67 bhvr 0 1 tick 400 mvto var0 var1 mesg writ targ 8 mesg writ targ 1 retn endm | scrp 2 4 14 1 doif obv2 eq 2 targ from doif fmly eq 4 targ ownr snde chwp stim writ from 10 255 0 0 …35 100 57 80 33 20 103 10 else targ ownr gsub frut endi targ ownr pose 8 setv obv0 0 bhvr 0 0 orrv attr 16 andv attr 251 setv var2 1 doif obv1 gt 3 rndv var2 1 2 doif var2 eq 2 setv var2 3 endi endi setv obv2 var2 endi subr frut setv var0 posl setv var1 post inst new: simp flwr 1 10 500 0 setv clas 33949952 setv attr 67 bhvr 0 1 tick 400 mvto var0 var1 mesg writ targ 8 mesg writ targ 1 retn endm |
Alright, there's a lot going on in this one, and hopefully my colorful scribbles up there make some degree of sense. Code highlighted in the same color, if not identical, at least performs more or less the same function.
As you can see, the green frut subroutine (the code for popping the seed off the plant) is almost completely unaltered; I moved the pink location-grabbing lines into it because their only purpose is to get a location at which to spawn a seed. The code in blue is just the main routine running (or "calling") the subroutine and setting the plant's sprite to show that the seed has been removed. The yellow, of course, ensures that pushing only works if the plant has a seed.
The red chunk basically says "if the flower has grown back more than a certain number of times, randomly pick either 1 (unpollinated) or 3 (dying) and set the plant status to that value." The thing is, the crossed-out line in the frut subroutine originally overwrote this bit so it would always be 1. I removed that line so the random selection actually sticks.
So what about the rest of the code I added and removed? Basically, the original functionality was that when a creature pushed the plant, if there was a seed, the seed would pop off, and if there wasn't, the creature would eat the (glycotoxin-laced!) beelacanth. I removed the "eat the plant" section entirely so that the beelacanth would function more like my updated herbs: the plant itself is inedible, but creatures can eat a "sprig" (or in this case seed) off it. The first big chunk of newly-added code there checks what actually pushed the plant: if it's a creature, instead of popping the seed off, it just feeds the seed directly to the creature. If it's not a creature (i.e. it's the hand), then it pops the seed off.
The second big chunk of new code is mostly to render the de-seeded plant uninteractable: it turns off activatability and turns on invisibility. The first line, where I set obv0, is to reset the fruit timer. In the original script, they didn't actually reset this variable, so a plant could only eject its seed on its own once (and the timer for that event was ridiculously long, so it basically never happened).
Original Plant Pull | New Plant Pull |
---|---|
scrp 2 4 14 2 doif fmly eq 4 rndv var0 0 20 targ ownr doif var0 eq 0 doif pose eq 5 setv obv2 2 endi setv actv 0 endi else targ ownr doif pose eq 5 setv obv2 2 endi setv actv 0 endm | scrp 2 4 14 2 doif pose eq 5 setv obv2 2 endi setv actv 0 endm |
The pull script is a much simpler story. The first big doif section checks whether a creature is what pulled the plant. If so, there's a complicated little bit of code to determine whether the creature pollinates the flower or not. If it's not a creature (i.e. it's a bee) and the plant is in the correct stage of growth, it pollinates the beelacanth, no questions asked. I removed the ability of creatures to pull the plant so I just chopped out the entire section of code for creatures pulling the plant.
The timer script is where most of the beelacanth magic happens, but I made surprisingly few changes to it. In fact, I'm not even going to show the old and new side by side; I'm just going to annotate the code with removed lines in gray, changed values in orange, and added sections in green. The timer script has doifs that divide it into three main sections, so I've sliced those up in this table for extra readability.
Timer Script | Explanation |
---|---|
scrp 2 4 14 9 doif obv2 le 1 doif pose lt 5 setv var0 pose addv var0 1 pose var0 endi doif pose eq 9 pose 5 endi doif pose eq 8 pose 9 addv obv1 1 endi endi | BEELACANTH TIMER SCRIPT if plant is unpollinated {if pose 0-4 (plant growing from seed) {get pose as variable increment variable set pose to incremented variable } if pose 9 (flower growing back) {set pose 5 (fully grown flower) } if pose 8 (seed just plucked) {set pose 9 (flower growing back) increment growback counter } } |
doif obv2 eq 3 doif pose eq 0 kill ownr endi doif pose le 7 setv var0 pose subv var0 1 pose var0 else pose 4 endi endi | if plant is dying { if pose 0 (plant just starting to grow) {plant reached end of dying stage, delete. } if pose 1-7 (growing, normal, fruiting) {get pose as variable decrement pose set pose as decremented variable }else (just plucked or growing back) {set pose 4 (final growing stage) } } |
doif obv2 eq 2 doif pose lt 7 setv var0 pose addv var0 1 pose var0 doif var0 eq 7 andv attr 239 orrv attr 4 bhvr 1 1 endi endi doif pose eq 7 addv obv0 1 doif obv0 gt 30 mesg writ ownr 0 endi endi doif pose eq 8 setv obv2 1 endi endi endm | if plant is pollinated {if pose 0-7 (has not reached full fruit) {get pose as variable increment variable set pose as incremented variable if plant is now fully fruited {turn off invisibility turn on activatability clickable, pushable } } if fully fruited {increment seed timer if timer has hit 30 (originally 1000) {run push script to trigger seed ejection } } if seed has just been removed {set as unpollinated } } |
Aside from the removal of a bhvr line in the dying plant section (we don't want it to be pushable when dying), all of my changes were in the pollinated plant section. All I really did here was make the seed timer trigger more frequently and add a section in the growing seed section to ensure that as soon as it hits the fully fruited stage, the plant becomes visible and pushable. As shown above, the matching code section for turning it invisible/unpushable happens in the push script.
If you're wondering why I didn't put the become-visible logic in the "if fully fruited" section... originally I did. There are two problems with this. One, a fully fruited plant will run that script every time its timer triggers, and when you run code more often than you need to, you get that many more chances for something to go wrong. And two, tying into that, it didn't reliably flag the plant as visible. Strictly speaking, since the "if fully fruited" section is another doif rather than an else, theoretically that code should run (assuming the conditional is true, of course) after the "if pose 0-7" section, but what I've found is that in practice it does not, or at least, not reliably.
The seeds also needed a few edits.
Seed Push Script | Explanation |
---|---|
scrp 2 6 9 1 stim writ from 10 255 0 0 …35 100 57 80 33 20 103 10 doif obv0 eq 3 stim writ from 10 255 0 0 …35 75 57 60 68 80 232 50 endi snde chwl targ ownr kill targ endm | 20 pain-- (was 50), 10 cough medicine (was 50) if fermented seed {give no stim, 75 hunger--, 60 starch, 80 alcohol, 50 histamine A } chew sound target seed delete seed |
The "pull" script for the seed is ingenious; since the seeds can't actually be pulled, the pull script is actually used to calculate the movement for bouncing (as C1 doesn't have a physics engine, so the motion of an object falling has to just be done by manually calculating and drawing each frame). That way, other scripts can just mesg writ a pull signal to the seed to make it bounce. I made no changes to the pull script, and it's mostly just a gigantic chunk of math to calculate the trajectory of the seed, so I won't annotate it here, and leave it as an exercise for the reader. Likewise, there are pickup and drop scripts that serve purely to flag the seed active or inactive so I won't show them, an a timer script, which is worth a look.
Seed Timer Script | Explanation |
---|---|
scrp 2 6 9 9 gsub seed setv var0 posl setv var1 post subv var1 20 doif obv0 eq 3 doif obv1 eq 1 new: simp flwr 10 0 510 0 setv clas 33820160 setv attr 80 bhvr 0 0 tick 200 mvto var0 var1 mesg writ targ 8 endi targ ownr kill targ endi addv obv0 1 | SEED TIMER SCRIPT run seed subroutine get left edge as var0 get top edge as var1 subtract 20 from var1 if seed age is 3 {if location is valid {create new flower it's a beelacanth wallbound and invisible (was 64, wallbound) not clickable, not usable (was 3, push/pullable) timer set to 200 move beelacanth to calculated location beelacanth enter scope script } target seed again destroy seed } if we get here, seed still exists, so add to age. |
subr seed setv obv1 0 doif posb lt 630 doif posb gt 480 doif posl gt 1656 doif posl lt 1752 setv obv1 1 endi endi doif posl gt 1168 doif posl lt 1500 setv obv1 1 endi endi doif posl gt 2168 doif posl lt 2456 setv obv1 1 endi endi endi endi doif posb lt 980 doif posb gt 800 doif posl gt 3116 doif posl lt 5916 setv obv1 1 endi endi endi endi retn endm | Calculate valid locations for the plant to grow: |
Once again, changes to this script were minimal. I just altered bhvr and attr values to ensure that the plant is properly invisible/uninteractable until it produces a seed.
Have you seen the Lifekit Tendril that's been recently uploaded on Eemfoo.org? ( https://eemfoo.org/archive/downloads/4567 ) It was supposed to come out with the cave flies. It injects into the jungle and simply kills any animal-genus thing (like the cave flies) that comes into contact with it.
ReplyDeleteInteresting. I may have to make a version that kills pests too.
Delete