Skip to content

Commit

Permalink
feat: improving StatusComponent including rename to EntityStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
kurtome committed Sep 1, 2024
1 parent 0d26a82 commit 7ff8b67
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 44 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ depend on the death animation finishing, you will also need to:

#### Status effect system

`PhysicalEntity` components can have statuses (`StatusComponent`) which modify
`PhysicalEntity` components can have statuses (`EntityStatus`) which modify
their behavior. Statuses affect the component they are added to. For example,
you could implement a `StarPowerStatus` which when added to your player
component makes them flash colors become invincible.
Expand All @@ -238,7 +238,7 @@ Since statuses are themselves components, they can maintain their own state and
handle updating themselves or their parent `PhysicalEntity` components. See
`OnLadderStatus` for an example of this.

There are mixins on `StatusComponent` which affect the Leap engine's handling of
There are mixins on `EntityStatus` which affect the Leap engine's handling of
the parent `PhysicalEntity`. See:

- `IgnoredByWorld`
Expand All @@ -249,7 +249,7 @@ the parent `PhysicalEntity`. See:
- `IgnoresNonSolidCollisions`
- `IgnoresSolidCollisions`

You can implement your own mixins on `StatusComponent` which control pieces of
You can implement your own mixins on `EntityStatus` which control pieces of
logic in your own game.

### Resetting the map on player death
Expand Down
1 change: 1 addition & 0 deletions examples/standard_platformer/lib/player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Player extends JumperCharacter
add(PlayerDamageBehavior());
add(PlayerInputBehavior());
add(PlayerCollisionBehavior());
add(OnLadderMovementBehavior());
add(PlayerDeathBehavior());
// Apply velocity to position (respecting collisions)
add(ApplyVelocityBehavior());
Expand Down
49 changes: 32 additions & 17 deletions packages/leap/lib/src/entities/ladder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ enum LadderMovement {

/// Status indicating the [PhysicalEntity] this is added to is
/// on a ladder.
class OnLadderStatus<T extends LeapGame> extends StatusComponent
with HasGameReference<T>, IgnoresGravity, IgnoresSolidCollisions {
class OnLadderStatus extends EntityStatus
with IgnoresGravity, IgnoresSolidCollisions {
OnLadderStatus(this.ladder);

final Ladder ladder;
Expand All @@ -62,26 +62,48 @@ class OnLadderStatus<T extends LeapGame> extends StatusComponent

LadderMovement _prevDirection = LadderMovement.stopped;
LadderMovement get prevDirection => _prevDirection;

/// Whether or not the entity on the ladder needs to be adjusted
/// to due to entering the ladder.
bool adjustEntry = true;
}

class OnLadderMovementBehavior extends PhysicalBehavior {
double moveSpeed = 0;

@override
void update(double dt) {
super.update(dt);
final ladderStatus = parent.getStatus<OnLadderStatus>();
if (ladderStatus == null) {
return;
}

final ladder = ladderStatus.ladder;

if (ladderStatus.adjustEntry) {
ladderStatus.adjustEntry = false;
// Update the y position to be fully on the ladder
if (parent.centerY < ladder.logicalTop) {
parent.centerY = ladder.logicalTop;
} else if (parent.centerY > ladder.bottom) {
parent.centerY = ladder.bottom;
}
}

if (!parent.collisionInfo.allCollisions.contains(ladder)) {
// No longer on the ladder
removeFromParent();
ladderStatus.removeFromParent();
parent.velocity.y = 0;
parent.velocity.x = 0;
} else if (parent.centerY < ladder.logicalTop &&
movement == LadderMovement.up) {
ladderStatus.movement == LadderMovement.up) {
// Over halfway off the top
parent.bottom = ladder.logicalTop;
removeFromParent();
ladderStatus.removeFromParent();
} else if (parent.centerY > ladder.bottom &&
movement == LadderMovement.down) {
ladderStatus.movement == LadderMovement.down) {
// Over halfway off the bottom
removeFromParent();
ladderStatus.removeFromParent();
} else {
// Still on the ladder.

Expand All @@ -90,7 +112,7 @@ class OnLadderStatus<T extends LeapGame> extends StatusComponent
parent.centerX = ladder.centerX;

// Update ladder y movement.
switch (movement) {
switch (ladderStatus.movement) {
case LadderMovement.up:
parent.velocity.y = -moveSpeed;
break;
Expand All @@ -108,13 +130,6 @@ class OnLadderStatus<T extends LeapGame> extends StatusComponent
@override
void onMount() {
super.onMount();
moveSpeed = game.world.gravity / 10;

// Update the y position to be fully on the ladder
if (parent.centerY < ladder.logicalTop) {
parent.centerY = ladder.logicalTop;
} else if (parent.centerY > ladder.bottom) {
parent.centerY = ladder.bottom;
}
moveSpeed = parent.leapGame.world.gravity / 10;
}
}
20 changes: 10 additions & 10 deletions packages/leap/lib/src/entities/physical_entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ abstract class PhysicalEntity extends PositionedEntity {
/// This is a list instead of a set for two reasons:
/// 1. For some uses status order could be important
/// 2. For some uses adding the same status twice could be valid
List<StatusComponent> get statuses => _statuses;
final List<StatusComponent> _statuses = [];
List<EntityStatus> get statuses => _statuses;
final List<EntityStatus> _statuses = [];

/// Position object to store the x/y components.
final Vector2 velocity = Vector2.zero();
Expand Down Expand Up @@ -330,25 +330,25 @@ abstract class PhysicalEntity extends PositionedEntity {
return solidTags.intersection(other.tags).isNotEmpty;
}

/// Invoked when a child [StatusComponent] is mounted, this is designed
/// to be called only by [StatusComponent.onMount]
void onStatusMount(StatusComponent status) {
/// Invoked when a child [EntityStatus] is mounted, this is designed
/// to be called only by [EntityStatus.onMount]
void onStatusMount(EntityStatus status) {
_statuses.add(status);
}

/// Invoked when a child [StatusComponent] is mounted, this is designed
/// to be called only by [StatusComponent.onRemove]
void onStatusRemove(StatusComponent status) {
/// Invoked when a child [EntityStatus] is mounted, this is designed
/// to be called only by [EntityStatus.onRemove]
void onStatusRemove(EntityStatus status) {
_statuses.remove(status);
}

/// Whether or not this has a status of type [TStatus].
bool hasStatus<TStatus extends StatusComponent>() {
bool hasStatus<TStatus extends EntityStatus>() {
return statuses.whereType<TStatus>().isNotEmpty;
}

/// Gets the first status having type [TStatus] or null if there is none.
TStatus? getStatus<TStatus extends StatusComponent>() {
TStatus? getStatus<TStatus extends EntityStatus>() {
return statuses.whereType<TStatus>().firstOrNull;
}
}
Expand Down
31 changes: 17 additions & 14 deletions packages/leap/lib/src/entities/status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@ import 'package:leap/leap.dart';
import 'package:leap/src/entities/physical_entity.dart';

/// A base for building status effects pertaining to [PhysicalEntity]. Effects
/// which could are reusable are typically implemented as mixins
/// (see [IgnoresGravity]), whereas fully custom statuses may simply extend this
/// and have no mixins.
/// could be implemented as mixins (see [IgnoresGravity]), whereas fully custom
/// statuses may simply extend this and have no mixins.
///
/// It is the responsibility of code affected by these status effects to check
/// if the entity has any relevant status before exucuting relevant logic.
class StatusComponent<T extends PhysicalEntity> extends PositionComponent
with ParentIsA<T> {
class EntityStatus extends Component {
// The entity this was added to, only valid when mounted
late PhysicalEntity entity;

@override
@mustCallSuper
void onMount() {
super.onMount();
parent.onStatusMount(this);
assert(parent is PhysicalEntity, 'parent must be PhysicalEntity');
entity = parent! as PhysicalEntity;
entity.onStatusMount(this);
}

@override
@mustCallSuper
void onRemove() {
parent.onStatusRemove(this);
entity.onStatusRemove(this);
super.onRemove();
}
}
Expand All @@ -32,28 +35,28 @@ class StatusComponent<T extends PhysicalEntity> extends PositionComponent
/// it will not be moved by gravity or velocity (unless you manually
/// update the position) and it will be completely ignored by the collision
/// system so nothing else will collide with it.
mixin IgnoredByWorld on StatusComponent {}
mixin IgnoredByWorld on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// be affected by gravity while the status is present.
mixin IgnoresGravity on StatusComponent {}
mixin IgnoresGravity on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// be automatically moved by its velocity.
mixin IgnoresVelocity on StatusComponent {}
mixin IgnoresVelocity on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// be eligible to collide with by others.
mixin IgnoredByCollisions on StatusComponent {}
mixin IgnoredByCollisions on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// collide attempt to collide with other things.
mixin IgnoresCollisions on StatusComponent {}
mixin IgnoresCollisions on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// collide with solids (ground).
mixin IgnoresSolidCollisions on StatusComponent {}
mixin IgnoresSolidCollisions on EntityStatus {}

/// A status mixin which indicates the parent entity should not
/// collide with non-solids.
mixin IgnoresNonSolidCollisions on StatusComponent {}
mixin IgnoresNonSolidCollisions on EntityStatus {}

0 comments on commit 7ff8b67

Please sign in to comment.