Cycle

The Scriptorium

Smart Assembly code templates and tools for on-chain development in Eve Frontier.

Advanced Combat Systems
AdvancedChapter 3 of 320 min read

Events and Integration

A combat system isn't useful in isolation. In this chapter, we'll add event emission for the killboard, integrate with other on-chain systems, and discuss testing strategies.

Combat Events

MUD auto-generates events when tables change, but we want custom semantic events for combat:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface ICombatEvents {
  event AttackResolved(
    uint256 indexed attackerShipId,
    uint256 indexed targetShipId,
    uint8 weaponSlot,
    uint32 damageDealt,
    uint8 damageType
  );

  event ShipDestroyed(
    uint256 indexed shipId,
    uint256 indexed destroyerShipId,
    uint256 timestamp
  );

  event ShieldsDepleted(
    uint256 indexed shipId,
    uint256 timestamp
  );
}

Emitting Events

Add event emission to the damage resolution:

solidity
contract CombatSystem is System, ICombatEvents {

  function resolveAttack(
    uint256 attackerShipId,
    uint8 weaponSlot,
    uint256 targetShipId,
    uint32 distance
  ) public returns (DamageResult memory) {
    // ... damage calculation from previous chapter ...

    emit AttackResolved(
      attackerShipId,
      targetShipId,
      weaponSlot,
      finalDamage,
      weapon.damageType
    );

    if (result.shieldDamage >= defenses.shieldHp) {
      emit ShieldsDepleted(targetShipId, block.timestamp);
    }

    if (result.destroyed) {
      emit ShipDestroyed(
        targetShipId,
        attackerShipId,
        block.timestamp
      );
    }

    return result;
  }
}

Killboard Integration

The killboard can listen for ShipDestroyed events to record kills:

typescript
import { createPublicClient, parseAbiItem } from "viem";

const client = createPublicClient({ / config / });

// Watch for ship destructions
client.watchEvent({
  event: parseAbiItem(
    "event ShipDestroyed(uint256 indexed shipId, uint256 indexed destroyerShipId, uint256 timestamp)"
  ),
  onLogs: (logs) => {
    for (const log of logs) {
      recordKill({
        victimShipId: log.args.shipId,
        killerShipId: log.args.destroyerShipId,
        timestamp: log.args.timestamp,
        blockNumber: log.blockNumber,
      });
    }
  },
});

Testing Complex Systems

For combat systems, comprehensive testing is critical:

solidity
contract CombatTest is Test {
  function test_fullCombatScenario() public {
    // Setup: two ships with known stats
    setupShip(ATTACKER, / weapon config /);
    setupShip(DEFENDER, / defense config /);

    // Round 1: Attack at optimal range
    DamageResult memory r1 = combat.resolveAttack(
      ATTACKER, 0, DEFENDER, 100 // within optimal
    );
    assertGt(r1.shieldDamage, 0);
    assertEq(r1.armorDamage, 0); // shields absorb all

    // Round 2: Attack at falloff range
    DamageResult memory r2 = combat.resolveAttack(
      ATTACKER, 0, DEFENDER, 500 // in falloff
    );
    assertLt(r2.shieldDamage, r1.shieldDamage); // less damage

    // Round 3: Weapon on cooldown
    vm.expectRevert("Weapon on cooldown");
    combat.resolveAttack(ATTACKER, 0, DEFENDER, 100);

    // Round 4: Wait for cooldown, finish the fight
    vm.warp(block.timestamp + 30); // skip cooldown
    DamageResult memory r4 = combat.resolveAttack(
      ATTACKER, 0, DEFENDER, 100
    );
    // Assert expected layer damage
  }
}

Design Patterns Recap

This combat system used several important patterns:

PatternWhere Used
StrategySwappable damage calculators per damage type
PipelineSequential damage resolution stages
ObserverEvent emission for external systems
Template MethodBase damage calculation with extension points

Congratulations!

You've completed the Advanced Combat Systems tutorial. You now know how to:

  • Design complex on-chain game systems
  • Implement multi-stage damage resolution
  • Use events to integrate with external systems
  • Write comprehensive tests for combat scenarios

These patterns apply beyond combat — any complex Smart Assembly with multiple interacting systems can use this architecture.

Sign in to track your progress.