Item¶
A collectable item, medal or crystal berry bound to a crystalbfflags slot whose item id is the entity.animid field. This extends the logic of an item entity.
Data Arrays¶
data[0]: The item type. NOTE: if theanimidis a money item id (MoneySmall,MoneyMediumorMoneyBig), this should always be 0 or 1 (standard or key item) because if it's not, the collection logic won't work as expecteddata[1]: If it's not negative, an event id that will be processed in an event command at the end of the input string when collecting the item. This is optional, nothing happens if it doesn't existdata[2]: If it's not 0, the item cannot be caught using the playerbeemerang. This is optional, the item can be caught if it doesn't exist.data[3]: The crystalbfflags slot if applicable (theanimidis 3)
Additional data¶
activationflag: Ifdata[0]isn't 3 (it's not a Crystal Berry) and the value is above 0, the flag whose slot is the value will be set to true when collecting the itemregaionalflag: Ifdata[0]isn't 3 (it's not a Crystal Berry) and the value isn't negative, the regionalflag whose slot is the value will be set to true when collecting the item
HasHiddenItem¶
For an entity.animid of 1 or 2 (key item or medal), the return value is the activationflag being positive and the coresponding flag slot being false.
MapControl.CreateEntities¶
- entity.
animstateis set to the entity.animid - entity.
animidis set todata[0] - entity.
itemis set to true (this makes it an item entity) - entity.
spritelocal position is set to (0.0, 0.5, 0.0)
EntityControl.CreateItem¶
This is a special way to create an EntityControl that will also give it this object type and set it as an item entity. It's a public static method on EntityControl meaning it can be called by anyone to create an item object out of thin air. It returns the newly created NPCControl and it takes in the starting position, the item type, the item id, the starting velocity and the amount of time in frames this should be collectable.
The very first thing this does is call CreateNewEntity with the name tempitem and add an NPCControl to it.
The rest is logic specific to CreateItem:
objecttypeis set toItementitytypeis set toObject- entity.
itemis set to true making it an item entity - entity.
animidis set to the item type - entity.
animstate, entity.itemstateand entity.basestateare all set to the item id touchcooldownis set to 30.0 (prevents to pickup the item in the first 30.0 frames)- All collisions between entity.
ccoland player.entity.ccolare ignored (the item will rely on thesecondcollcreated later) - The position and entity.
startposare set to the starting position - The NPCControl gets childed to the map
insideidis set to the current onetimeris set to the timer senttempobjectis set to true (prevents the Crystal Berry SetUp logic from applying as this method already does it partially and EntityControl's startup will do the rest via CheckSpecialID)data[0]is set to the item typebouncesis set to 0- entity.
ongroundis set to true - entity.
startvelocityis set to the starting velocity - If the item type is 3 (Crystal Berry),
data[0]is overriden to be the item id instead (which simulates how this happens normally withdata[3]) and entity.animidis set to 43 (CrystalBerry)
Start¶
Normally, any objects has their entity.ccol disabled on Start, but this one is an exception: it remains enabled.
Also, just like SemiNPC items, no changes to the entity.rigid mass happens so it is left to 1.0.
SetUp¶
- entity.
ongroundis set to false - entity.
alwaysactiveis set to true - entity.
ccolbounciness is set to 0.75 - The layer is set to 0 (default)
- All collisions between entity.
ccoland these entity'sccolare ignored:- Objects with tag
PFollower - Any other Item objects (excludes
SemiNPC) - Any playerdata except the first one
- Objects with tag
If the entity.animid is 3 (which means it's a crystal berry) and tempobject is false, then this is setup as a crystal berry which starts by setting data[0] to data[3] (meaning data[0] is now the Crystal Berry id which is fine because entity.animid already tells it's a Crystal Berry). If it was obtained (the id is contained in data[3]), then the entity.iskill is set to true. Otherwise, adjustements on the entity are performed:
- entity.
animstateis set to theregionaflagNOTE: this is likely an error, but it doesn't matter because the crystalbfflags slot is already saved indata[0]and it is that value that will be used - entity.
spritelocal position is set to Vector3.zero - AddModel is called with path
Prefabs/Objects/CrystalBerrywithout offset - entity.
spinis set to (0.0, 2.0, 0.0) - entity.
itemis set to true which makes it an item entity - entity.
rigidgravity is enabled
AddPlayerTrigger is called which will initialise secondcoll to a new trigger CapsuleCollider with the same properties than the entity.ccol and ignores all collisions between the player.entity.ccol and the entity.ccol.
Just like SemiNPC items, the colliderheight is set to 1.0 and the entity.ccol center to (0.0, 0.5, 0.0). This reduces the height of the collider to more snuggly fit the item shape.
Update (active and not trapped)¶
This section applies for any item entity, but it effectively apply to this object type. Unlike SemiNPC items however, this gets the full version of the logic.
If the beerang isn't null (meaning the beemerang caught this item), the following occurs:
- The
timeris set to 300.0 if it was -1 - The absolute position is set to the
beerangone + Vector3.Up - The entity's Unifx gets called if it was a
fixedentity
All collisions gets ignored between the secondcoll and the player.entity.ccol if the player is present and the touchcooldown hasn't expired yet. Otherwise, if the touchcooldown isn't exactly -9999.0, the collisions gets unignored and toochcooldown gets set to -9999.0 (so it doesn't unignore again). If neither happened, the entity.ccol is enabled.
If the entity.onground is true, Jump is called on it with the absolute value of its current rigid y velocity. It will also increment bounces if it hasn't reached 3 yet on top of playing the ItemBounce0 sound effect (or ItemBounce1 if it's a Crystal Berry) if the startlife is above 15 frames.
If by then, bounces has reached 3, StopForceMove is called on the entity.
If the entity.sprite is present, then the timer logic proceeds:
- If the
timerhas yet to expire and we aren't in aminipauseorpause, the timer is decremented according to the frametime, but it is clamped from 0.0 to infinity. Otherwise, if thetimerexpired, this GameObject gets destroyed - If the
timeris between -1.0 and 100.0 exclusive and we aren't in aminipauseorinevent, the entity.spriteenablement gets toggled. Otherwise, the entity.spriteis enabled. This logic blinks the sprite when less than 100 frames are left on thetimer
OnTriggerEnter if the other collider is the player¶
If we aren't in a pause or minipause, the insideid matches the current one and the timer is exactly -1.0 or above 1.0, a CheckItem coroutine is started and collisionammount is incremented.
OnTriggerEnter main segment¶
The beerang is set to the other transform if all of the following are true (this makes the Beemerang catch the item):
- The other gameObject tag is
BeeRang(meaning the other collider is the Beemerang) data[2]is either not present or it is and it's value is 0 (meaning it's a standard item)beerangwas null (theBeemerangdidn't already caught an item)- We aren't in a
pause, orminipauseand message is released - The player
beemerangis present and itshitis false (it's on the first half of its path) insideidmatches the current one
OnTriggerStay (when the NPCControl is a JumpSpring)¶
If the other collider is an item entity and is an Object (which can only happen with this object type under normal gameplay), the item is allowed to move upward using the spring if its data[0] is 0 or its data[1] is 1 and the additional check it provides is fufilled.
CheckItem¶
This is a private coroutine that is only involved with this object type. Its job is to setup and confirm that the item can be collected followed by a bunch of logic performing the actual collection of the item.
- The game determines if this is a special currency item. This is the case if entity.
animstate(the item id) is 6 (MoneySmall), 7 (MoneyMedium) or 186 (MoneyBig). This will be used for many checks later - All frames are yielded while we are in a
pauseorminipauseandhitis false (the item isn't collected). This never happens under normal gameplay because the caller (OnTriggerEnter) already makes sure that we aren't in apauseorminipause - CancelAction is called on the player for anything other than standard money items
- instance.
itempickedis set to true (controls some logic related to JumpSpring objects and Shop / CaravanBadge interactions) - If it's not a money item, player.entity.
icooldownis set to 0.0 (removes any invicibily frames) hitis set to true (indicating the item is being collected)tossedis set to false- The current player.entity.
flipis saved locally as it may be restored later - All collisions between the item entity and the player.entity are ignored for any colliders
- The item collection occurs if the
touchcooldownexpired, the item isn'ttrappedand we aren't in aminipause. NOTE: this seems like a bug because if we don't collect here, the colliders aren't unignored, but in practice, MainManager.IgnoreColliders doesn't work correctly meaning this isn't an issue in practice. See the sections below for more details. The collection logic depends on if the item is a standard or key money item. - instance.
itempickedis set to false
Standard or key money items collection¶
- The regionaflag whose slot is
regionalflagis set to true if it wasn't negative - The flag whose slot is
activationflagis set to true if it was above 0 touchcooldownis set to 999999.0- instance.
moneyis incremented by the amount the item is worth in currency (1 forMoneySmall, 5 forMoneyMediumand 20 forMoneyBig) - instance.
showmoneyis set to 150.0 frames which shows the berry count HUD temporarily - The
Moneysound is played - entity.
spinis set to (0.0, 30.0, 0.0) - A BerryBounce coroutine is started. This just renders the visual effect of collecting the berry by making its sprite go up and scale down
dummyis set to true (disables most update cycles logic as BerryBounce needs some time before destroying the item so most updates needs to not run while this happens)- instance.
moneyis clamped from 0 to 999 - entity.
iskillis set to true
Any non money items collection¶
This logic is quite complex and has different behaviors depending on the entity.animid and entity.animsate.
Hold the player until they get onground¶
The logic first starts to handle the case where the player is still in the air:
- player.
lockkeysis set to true (disable most input processing on PlayerControl) - As long as player.entity.
ongroundis false:- player.entity.
rigidx/z velocity is zeroed out - instance.
minipauseis set to true - The position of the item is set to offscreen at (0.0, 999.0, 0.0)
- If we have been on this yield loop for 300.0 frames or more, we force exit it by setting the player position to its
lastposwith DeathSmoke particle playing at the player position - A local framecounter is incremented by
framestepwhich only takes effect once it reaches 300.0 as outlined in the step above - A frame is yielded
- player.entity.
- player.
lockkeysis set to false (unlocks most input processing of PlayerControl)
Item collection perparation¶
From there, the actual item collection can now take place:
beerangis set to null (detaches the item off the Beemerang if it was attached)- entity.
spingets zeroed out - entity.
spriteangles gets set to Vector3.zero - CancelAction is called on the player
- entity.
ccolgets disabled - entity.
rigidgets its gravity disabled with all constraints frozen - The item gets childed to the player.entity.
spritewith a local position of (0.0, 2.25, -0.1) - flagvar 0 gets set to entity.
animstate(the item id) - A
guisprites[85](star shaped back icon) gets created with namebackchilded to entity.spritewith the same material as entity.spritewithout angles with local position (0.0, 0.0, 0.2) with scale of Vector3.zero with a DialogueAnim and layer 14 (Sprite) - flagstring 1 gets set to
menutext[125](a).
Item type specifics¶
What happens next depends on entity.animid (the item type) and it mainly sets the back material color, sets flagstring 0, flagstring 1 and other exclusive logic to an item type:
- 0 or 1 (standard or key item):
flagstring0 is set to the name andflagstring1 to the prepender of the corresponding item defined in items data using the entity.animstateas the item id.- It also sets the
back's material color to 00B2B2 (cyan) if it's a standard item, FF4C66 (bright red) if it's a key item
- 2 (medal):
- If
flags681 (MYSTERY? is enabled) and entity.animstateisn't 59 (it's notExtra Freeze) or it is, butflags696 (MYSTERY? file started on 1.1.x) is false:- The next mystery medal id is obtained and the value is set to entity.
basestate, entity.itemstate, entity.animstateandflagvar0 - entity.
overridemovesmokeis set to true and UpdateItem is called on the entity (to ensure the actual sprite gets updated)
- The next mystery medal id is obtained and the value is set to entity.
- The
backmaterial color gets set to FF7F00 (bright orange) flagstring0 is set to a string obtained from takingmenutext[268]and replacingiby the corresponding medal name from medal data using entity.animstateas the medal id and by replacingmbymenutext[159](Medal)flagstring1 is set to the corresponding prepander from medal data using entity.animstateas the medal id
- If
- 3 (Crystal Berry):
- The
backlocal position is set to (0.0, 0.5, 0.0) and its material color to cyan - entity.
modely scale is set to 0.1 - crystalbfflags whose slot is
data[0](used to bedata[3]which is the slot number) is set to true flagstring0 is set tomenutext[112](Crystal Berry)flagvar14 (amount of Crystal Berries obtained) gets incremented
- The
After this, for any type other than Crystal Berry, CreateDescWindow without shop is called.
For any type, the ItemGetX sound is played where X is the entity.animid (the item type).
SetText call¶
The next portion setups a SetText call to actually receive the item. The SetText string is built with the following concatenated in order:
|lockmovement||boxstyle,4||halfline||spd,0||anim,-1,4||center|menutext[2](You found |string,1| |color,1||string,0||color,1|!)|stopskip||fwait,0.45||break|- If it's not a Crystal Berry and
activationflagis above 0,|flag,activationflag,true|is appended - If it's not a Crystal Berry and
regionalflagis not negative,|regionalflag,regionalflag,true|is appended |additemtoss,- entity.
animid(the item type) ,var,0|- If
flags31 is false and it's a medal (haven't received the medal tutorial while getting one),|flag,31,true||tail,null||center,true||destroydescbox||goto,-32,break,end|is appended - If
flags108 is false and it's a Crystal Berry (haven't received the Crystal Berry tutorial while getting one),|flag,108,true||tail,null||center,true||destroydescbox||goto,-88,break,end|is appended - If
data[1]exists and isn't negative,|event,+data[1]+|is appended (a break command appears before the event one if any of the tutorial strings were appended)
SetText is called with the generated string in dialogue mode:
- fonttype of
BubblegumSans - linebreak of instance.
messagebreak - No tridimensional
- No position offset
- No camera offset
- Size of Vector2.one
- No parent
- This NPCControl as the caller
SetText yield¶
This is the final part part of the collection phase:
timeris set to -1 (so it never times out)- entity.
overrideflipis set to true - As long as the message lock is active:
- player.entity.
flipis set to the value it had before this entire collection phase at the start of CheckItem - player.entity.animsate is set to 4 (
ItemGet) - A frame is yielded
- player.entity.
- If the item isn't
tossed(it is only when additemtoss concluded the player wanted to toss the item), this gameObject is destroyed
EntityControl.LateStart¶
Any object of this type will have CreateFeet called which gives the entity a ground detector in the feet field.
Hazards.OnObjectStay¶
If the NPCControl passed has this type, it returns true which allows it to stay in collision with the Hazards.
SetText¶
This object type affects SetText if it's the caller:
- In an end command, if the caller is an
Item, Death is called on the entity - In the cleanup phase, if the caller is an object of this type and its
hitis true (meaning it's been collected), its gameObject is destroyed.