Search This Blog

Bees, Beelacanths, and Flies, Oh My!

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 PullNew 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.

Download Updated Cave Fly

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 PushNew 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

else
 snde chwp
 stim writ from 10 255 0 0
  …57 30 20 50 67 5 0 0
 kill ownr

endi








subr frut
setv obv2 1

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 PullNew Plant Pull
scrp 2 4 14 2
targ from
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
endi
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 ScriptExplanation
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
 bhvr 0 1
 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
{unclickable, pushable. Removed line.
 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 ScriptExplanation
scrp 2 6 9 1
doif obv0 lt 3
 stim writ from 10 255 0 0
  …35 100 57 80 33 20 103 10
endi
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

if fresh seed
{give no stim, 100 hunger--, 80 starch,
 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 ScriptExplanation
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.

2 comments:

  1. 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.

    ReplyDelete
    Replies
    1. Interesting. I may have to make a version that kills pests too.

      Delete