Barrels/Baskets

Idle appearance of a barrel and a basket.

The Barrels and Baskets (shortened to just “barrels” when repetition gets tedious) are two visually distinct types of object that function identically. In their initial state, they sit on the ground and appear as static, motionless metallic drums or woven fabric squares. Each barrel releases a different predefined actor when destroyed, either as a result of the player pouncing on it or due to an explosion in close proximity. All of the barrels encountered in an unmodified game will release a prize of some sort, although unusual and unused barrel types do exist.

Harmfulno
Pounceableyes
Pounces Required1
Pounce Points100
Explodableyes
Explosions Required1
Explosion Points1,600
Name OriginCanonical; appears in the hint sheet text.

Data Fields

data1
The actor type that will be spawned when the barrel is destroyed. This actor should have its “always active” and “weighted” flags enabled to allow the actor to potentially spawn off the top edge of the scrolling game window and fall back down.
data2
The sprite type that will be shown as shards when the barrel is destroyed. This sprite type is expected to have at least four frames available. This value should be SPR_BARREL_SHARDS or SPR_BASKET_SHARDS.

Initial Values

Barrels

Actor Type
Sprite TypeSPR_BARREL
X Shift0
Y Shift0
Force Activeyes
Stay Activeno
Weightedyes
Acrophilenoyesno
Data 1
Data 2SPR_BARREL_SHARDS
Data 30 (not used)
Data 40 (not used)
Data 50 (not used)

Note: The differences in the acrophile flag do not matter since barrels do not walk.

Baskets

Actor Type
Sprite TypeSPR_BASKET
X Shift0
Y Shift0
Force Activeyes
Stay Activeno
Weightedyesnoyes
Acrophileno
Data 1
Data 2SPR_BASKET_SHARDS
Data 30 (not used)
Data 40 (not used)
Data 50 (not used)

Note: The differences in the weighted flag could have a visible effect, in the case of e.g. a Basket (contains three-fingered yellow/cyan fruit) sitting on a moving platform – it would not descend along with the platform. This is a moot issue in practice since this actor type is unused.

Player Interaction

Inside the InteractPlayer() function, this case occurs every time a barrel actor is processed:

    case SPR_BASKET:
    case SPR_BARREL:
        if (act->hurtcooldown == 0 && TryPounce(5)) {
            DestroyBarrel(index);
            AddScore(100);
            NewActor(ACT_SCORE_EFFECT_100, act->x, act->y);
            return true;
        }

        return false;

This responds to actors having the SPR_BASKET or SPR_BARREL sprite types. If the barrel has a hurtcooldown of zero, it is eligible to be pounced and TryPounce tests to see if the player is appropriately positioned to be pouncing on it at this time. A recoil value of 5 is imparted to the player if the pounce succeeds, then the if’s body runs.

When a barrel is pounced, DestroyBarrel() is called with index (as passed into InteractPlayer()) as the argument to destroy the barrel and spawn its contents into the game world. AddScore() gives the player 100 points and NewActor() spawns a Floating Score (ACT_SCORE_EFFECT_100) at the barrel’s final x/y position to call attention to this.

InteractPlayer()’s convention is to return true in cases where the actor should not be drawn for this tick (here because it has been destroyed) and false in the common case where nothing has changed with the actor’s state.

ActBarrel()

ActBarrel() is the tick function for every barrel and basket actor. It takes the index of the current actor in the actors array.

void ActBarrel(word index)
{
    Actor *act = actors + index;

The passed index is added to the actors[] array, locating the Actor structure for the barrel being processed. act is the pointer to this actor.

    if (IsNearExplosion(SPR_BARREL, 0, act->x, act->y)) {
        DestroyBarrel(index);
        AddScore(1600);
        NewActor(ACT_SCORE_EFFECT_1600, act->x, act->y);
    }
}

Barrels can be destroyed by explosions, and IsNearExplosion() will return true if one is currently nearby. For the purposes of this test, the actual barrel/basket type is temporarily ignored and the intersection is tested as if every actor was showing frame zero of SPR_BARREL. This is not a problem in the horizontal direction because barrel and basket sprites are both four tiles wide. Vertically, the barrels are one tile taller than the baskets – this could cause an explosion to register a hit to the top of a basket even though it visually misses it by one tile.

If an explosion is touching the barrel, DestroyBarrel() is called with index as the argument to destroy the barrel and spawn its contents into the game world. AddScore() gives the player 1,600 points and NewActor() spawns a Floating Score (ACT_SCORE_EFFECT_1600) at the barrel’s final x/y position to call attention to this.

DestroyBarrel()

The DestroyBarrel() function handles the animation, sound effects, and lifecycle management at the instant a barrel or basket is destroyed.

void DestroyBarrel(word index)
{
    Actor *act = actors + index;

    act->dead = true;

The passed index is added to the actors[] array, locating the Actor structure for the barrel being processed. act is the pointer to this actor.

Setting the barrel’s dead to true effectively removes it from the map and from all subsequent processing. Its life has now ended.

    NewShard(act->data2, 0, act->x - 1, act->y);
    NewShard(act->data2, 1, act->x + 1, act->y - 1);
    NewShard(act->data2, 2, act->x + 3, act->y);
    NewShard(act->data2, 3, act->x + 2, act->y + 2);

Four shards are released around the barrel’s final x/y position with NewShard(), using the value in data2 to select between barrel or basket shards as appropriate for the actor type. Frames 0–4 of these sprite types contain different “strips” of debris rather than anything that could be considered an animation.

    if (GameRand() % 2 != 0) {
        StartSound(SND_BARREL_DESTROY_1);
    } else {
        StartSound(SND_BARREL_DESTROY_2);
    }

There are two sound effects, SND_BARREL_DESTROY_1 and SND_BARREL_DESTROY_2, selected with 50/50 probability based on the GameRand() result. Whichever sound effect is chosen is queued for playback with StartSound().

    NewSpawner(act->data1, act->x + 1, act->y);

data1 is the actor type of the object that the barrel releases. NewSpawner() begins spawning it into existence starting at the barrel’s final y position and one tile to the right of x. This perfectly centers any sprites that happen to be two tiles wide.

    if (numBarrels == 1) {
        NewActor(ACT_SPEECH_WOW_50K, playerX - 1, playerY - 5);
    }
    numBarrels--;
}

In the case where numBarrels equals 1, this was the last remaining barrel and/or basket on the map. This is worthy of a score bonus, which is granted indirectly by inserting a “Speech Bubble: Wow! 50,000 points!” (ACT_SPEECH_WOW_50K) actor via NewActor(). This new actor is centered relative to the player, since this is a speech bubble coming out of them.

In all cases, numBarrels is decremented to keep the count accurate to the remaining barrel population.