Implement Klad1 bricks so that:
- bullets damage bricks in 25% sections
- bullets visually travel through already damaged brick space
- bricks block the player until fully destroyed
- fully destroyed bricks regenerate one stage every 6 seconds
- the model is clean enough that later behavior changes do not break everything
This is one of the most sensitive gameplay systems, so the logic should be correct first and the rendering should follow the logic, not the other way around.
Each brick has 4 virtual sections:
[I|I|I|I]
Where:
I= intactD= damaged / transparent to bullets
Each section represents 25% of brick width.
If a bullet comes from the right:
[I|I|I|I] -> [I|I|I|D] -> [I|I|D|D] -> [I|D|D|D] -> [D|D|D|D]
If a bullet comes from the left:
[I|I|I|I] -> [D|I|I|I] -> [D|D|I|I] -> [D|D|D|I] -> [D|D|D|D]
Bullets pass through damaged sections.
That means:
- if 1 quarter is damaged, bullet passes through 25% of brick width
- if 2 quarters are damaged, bullet passes through 50% of brick width
- if 3 quarters are damaged, bullet passes through 75% of brick width
- bullet stops only at the first intact section
This is the most important interaction rule.
Player collision is different from bullet collision:
- player is blocked while brick is not fully destroyed
- player can cross only when brick is fully destroyed
- regenerating brick is crossable until fully restored
So:
- bullets care about per-quarter damage
- player cares only whether the brick still has any intact section
Brick should store logical state, not just a sprite id.
Recommended fields:
int damagedLeft = 0;
int damagedRight = 0;
Uint64 destroyedAt = 0;
bool regenerating = false;Derived values:
int getDamagedCount() const;
int getIntactCount() const;
bool isDestroyed() const;
bool isSolid() const;Definitions:
getDamagedCount() = damagedLeft + damagedRightgetIntactCount() = 4 - getDamagedCount()isDestroyed() = getIntactCount() == 0isSolid() = getIntactCount() > 0
The gameplay rules allow mixed states, for example:
[D|I|I|D]
[D|D|I|D]
[D|I|D|D]
Current art only supports:
- left-only damage:
121314
- right-only damage:
181920
There are no sprites for mixed partial states.
So the logic must support mixed states even if the initial rendering does not.
This is why logic should not be based on sprite ids.
We are not fully covering mixed states yet.
Current asset decision:
[D|I|I|D]now exists as a real appended atlas tile[D|D|I|D]temporarily renders as blank[D|I|D|D]temporarily renders as blank
The new mixed tile was built from:
- left half of tile
12 - right half of tile
18
and appended to the atlas as:
TILE_BRICK_DIID = 46
This is intentionally temporary. Logic should still support all mixed states, even if the rendering falls back for two of them.
Suggested methods:
enum class HitSide {
Left,
Right
};
class Brick : public Unit {
public:
bool hit(HitSide side, float& hitX);
bool isSolid() const;
bool isDestroyed() const;
int getSpriteTile() const;
int getRegenTile() const;
float getQuarterWidth() const;
float getHitX(HitSide side) const;
void update(Context* context) override;
void render(Context* context) override;
int getType() const override;
bool canCrossUnit(Unit* target) const override;
};Meaning:
hit(...)applies one section of damage from left or righthitXreturns where the bullet should stop visuallyisSolid()tells whether player should be blockedupdate(...)handles regenerationrender(...)handles damage/regeneration visuals
Current bullet behavior is too simple:
- move
- overlap
- die
That is not enough for quarter-damaged bricks.
Instead, bullet should:
- compute horizontal travel for this frame
- search for the earliest hit in that direction
- possible hits:
- wall tile
- brick intact section
- stop at the earliest hit point
- if brick hit:
- damage that section
- die
- if wall hit:
- die
Let:
float quarter = brickWidth / 4.0f;The first intact section starts after damaged-left sections:
hitX = brickX + damagedLeft * quarter;That is the left edge of the first intact quarter.
The first intact section from the right starts here:
hitX = brickX + brickWidth - damagedRight * quarter;More precisely, the bullet should stop at the edge of the first intact quarter from that side.
So the bullet should not stop at brick bounds blindly. It should stop at the first intact quarter edge.
Regeneration starts only when the brick becomes fully destroyed.
Recommended behavior:
- on the 4th damage hit:
- brick becomes destroyed
- set
regenerating = true - store
destroyedAt = now
Then regeneration stage advances every 6 seconds.
0-6s blank
6-12s TILE_BRICK_REGEN_1 = 15
12-18s TILE_BRICK_REGEN_2 = 16
18-24s TILE_BRICK_REGEN_3 = 17
24s+ full brick
This is the clean implementation of:
- every 6 second next regen stage
Derived stage:
int stage = elapsedMs / 6000;Clamped to:
0blank1regen 12regen 23regen 34full brick
At full regeneration:
damagedLeft = 0damagedRight = 0regenerating = false
and the brick becomes solid again.
There are 2 possible rendering strategies.
Use:
8full brick12,13,14left-only damage18,19,20right-only damage46for[D|I|I|D]15,16,17regen- blank when fully destroyed
Problem:
- two mixed states still do not have sprites:
[D|D|I|D][D|I|D|D]
So this option is incomplete unless mixed states are forbidden or visually approximated.
Render brick by quarters:
- 4 vertical slices
- each slice either intact or empty
- use normal brick art for intact quarters
- overlay regen sprites when needed
This matches gameplay much better and supports mixed states naturally.
Recommended long-term choice:
- logic always uses quarter state
- rendering can start simple and later move to quarter rendering
Implement on Brick:
- quarter damage state
hit(...)isSolid()- regeneration timing
No fancy rendering yet.
Change bullet update so it:
- detects wall hit
- detects brick quarter hit
- stops at the correct hit point
- damages brick
- dies
This is the first important gameplay milestone.
Make sure:
- partially damaged brick still blocks player
- fully destroyed / regenerating brick is crossable
- fully regenerated brick blocks again
Start with simplest useful visuals:
- full brick
8 - left-only damage
12,13,14 - right-only damage
18,19,20 [D|I|I|D]uses46[D|D|I|D]uses blank temporarily[D|I|D|D]uses blank temporarily- blank when destroyed
- regen
15,16,17
If mixed states look wrong, move to quarter rendering.
Current Klad1 atlas now includes one additional mixed brick frame:
46=[D|I|I|D]
It was appended after the existing atlas content, so no older frame indices changed.
Temporary policy:
[D|I|I|D]-> render46[D|D|I|D]-> render blank[D|I|D|D]-> render blank
This is only a rendering fallback. The logical damage state must still remain correct.
Do we accept the temporary visual lie for:
[D|D|I|D][D|I|D|D]
while keeping gameplay correct?
Current assumption:
- yes
When a mixed damaged brick is fully destroyed and starts regenerating, do we care about preserving the old damage-side visual history?
Current assumption:
- no
- once destroyed, regeneration uses only:
- blank
151617- full brick
Implementation still needs a precise rule for “earliest hit wins” when a bullet could hit:
- a wall tile
- a brick intact quarter
in the same frame.
Current assumption:
- compare horizontal hit distance this frame
- whichever comes first wins
The plan still should use:
- query hit point first
- mutate brick second
Recommended final shape remains:
float getHitX(HitSide side) constvoid hit(HitSide side)
instead of one method that both computes and mutates.
Sprite should be derived from state, not be the state.
Bad:
tile == 13 means brick has this logic
Good:
damagedLeft = 2damagedRight = 0- render chooses sprite
13
Bullet must test:
- travel path
- first intact quarter
- not just final overlap
So bullet-brick interaction should be treated as a custom gameplay hit.
Player:
- blocked until fully destroyed
Bullet:
- passes through damaged quarters immediately
If those two ideas get mixed into one collision rule, the system will become confusing.
The first good checkpoint is:
- bullet damages brick quarter by quarter
- bullet dies on wall or brick
- player is still blocked until brick is fully destroyed
- fully destroyed brick becomes blank and starts regenerating
That gives a working gameplay loop before advanced rendering polish.