183 lines
4.9 KiB
Plaintext
183 lines
4.9 KiB
Plaintext
-- allium: 3
|
|
-- allium: magical-objects
|
|
|
|
-- Scope: Magical Objects (Healing Objects and Weapons)
|
|
-- Includes: MagicalObject base, HealingObject, MagicalWeapon, Character interactions
|
|
-- Excludes:
|
|
-- - Magical Object to Magical Object interactions (not in story)
|
|
-- - Magical Object factions (they are neutral)
|
|
-- - Characters healing Magical Objects (forbidden by story)
|
|
|
|
------------------------------------------------------------
|
|
-- Entities and Variants
|
|
------------------------------------------------------------
|
|
|
|
entity MagicalObject {
|
|
health: Health
|
|
maxHealth: Health
|
|
status: alive | destroyed
|
|
|
|
is_alive: status = alive
|
|
is_destroyed: status = destroyed
|
|
|
|
transitions status {
|
|
alive -> destroyed
|
|
terminal: destroyed
|
|
}
|
|
}
|
|
|
|
entity HealingObject : MagicalObject {
|
|
-- Healing objects transfer health to characters
|
|
-- They cannot deal damage
|
|
}
|
|
|
|
entity MagicalWeapon : MagicalObject {
|
|
damage: Integer -- fixed damage amount
|
|
owner: Character -- only the owner can use this weapon
|
|
-- Weapons cannot give health to characters
|
|
}
|
|
|
|
------------------------------------------------------------
|
|
-- Rules
|
|
------------------------------------------------------------
|
|
|
|
rule HealingObjectHealsCharacter {
|
|
when: CharacterUsesHealingObject(character, object, amount)
|
|
|
|
requires: object.status = alive
|
|
requires: character.status = alive
|
|
requires: amount >= 0
|
|
|
|
let objectRemaining = object.health
|
|
let characterMax = Level.maxHealthForLevel(character.level)
|
|
let characterHeadroom = characterMax - character.health
|
|
let actualHeal = min(amount, objectRemaining, characterHeadroom)
|
|
|
|
ensures:
|
|
if actualHeal > 0:
|
|
character.health = character.health + actualHeal
|
|
object.health = object.health - actualHeal
|
|
if object.health = 0:
|
|
object.status = destroyed
|
|
else:
|
|
object.status = alive
|
|
else:
|
|
character.health = character.health
|
|
object.health = object.health
|
|
}
|
|
|
|
rule HealingObjectDestroyedCannotHeal {
|
|
when: CharacterUsesHealingObject(character, object, amount)
|
|
|
|
requires: object.status = destroyed
|
|
|
|
ensures:
|
|
character.health = character.health
|
|
object.health = object.health
|
|
}
|
|
|
|
rule DeadCannotUseHealingObject {
|
|
when: CharacterUsesHealingObject(character, object, amount)
|
|
|
|
requires: character.status = dead
|
|
|
|
ensures:
|
|
character.health = character.health
|
|
object.health = object.health
|
|
}
|
|
|
|
rule HealingObjectZeroHealIsNoOp {
|
|
when: CharacterUsesHealingObject(character, object, amount)
|
|
|
|
requires: object.status = alive
|
|
requires: character.status = alive
|
|
requires: amount >= 0
|
|
requires: min(amount, object.health, Level.maxHealthForLevel(character.level) - character.health) = 0
|
|
|
|
ensures:
|
|
character.health = character.health
|
|
object.health = object.health
|
|
}
|
|
|
|
rule MagicalWeaponDealsDamage {
|
|
when: CharacterUsesWeapon(owner, weapon, target)
|
|
|
|
requires: weapon.status = alive
|
|
requires: owner.status = alive
|
|
requires: owner = weapon.owner
|
|
|
|
ensures:
|
|
target.health = max(0, target.health - weapon.damage)
|
|
if target.health = 0:
|
|
target.status = dead
|
|
weapon.health = weapon.health - 1
|
|
if weapon.health = 0:
|
|
weapon.status = destroyed
|
|
else:
|
|
weapon.status = alive
|
|
}
|
|
|
|
rule DeadCannotUseWeapon {
|
|
when: CharacterUsesWeapon(owner, weapon, target)
|
|
|
|
requires: owner.status = dead
|
|
|
|
ensures:
|
|
target.health = target.health
|
|
weapon.health = weapon.health
|
|
}
|
|
|
|
rule NonOwnerCannotUseWeapon {
|
|
when: CharacterUsesWeapon(thief, weapon, target)
|
|
|
|
requires: thief != weapon.owner
|
|
|
|
ensures:
|
|
target.health = target.health
|
|
weapon.health = weapon.health
|
|
}
|
|
|
|
rule WeaponDestroyedCannotDealDamage {
|
|
when: CharacterUsesWeapon(owner, weapon, target)
|
|
|
|
requires: weapon.status = destroyed
|
|
|
|
ensures:
|
|
target.health = target.health
|
|
weapon.health = weapon.health
|
|
}
|
|
|
|
------------------------------------------------------------
|
|
-- Invariants
|
|
------------------------------------------------------------
|
|
|
|
invariant MagicalObjectHealthNonNegative {
|
|
for m in MagicalObjects:
|
|
m.health >= 0
|
|
}
|
|
|
|
invariant MagicalObjectHealthNeverExceedsMax {
|
|
for m in MagicalObjects:
|
|
m.health <= m.maxHealth
|
|
}
|
|
|
|
invariant MagicalObjectDestroyedAtZeroHealth {
|
|
for m in MagicalObjects:
|
|
m.health = 0 implies m.status = destroyed
|
|
}
|
|
|
|
invariant MagicalObjectAliveAtPositiveHealth {
|
|
for m in MagicalObjects:
|
|
m.health > 0 implies m.status = alive
|
|
}
|
|
|
|
invariant HealingObjectDoesNotDealDamage {
|
|
for h in HealingObjects, t in Characters:
|
|
not exists amount: CharacterUsesHealingObject(t, h, amount) implies t.health >= t.health
|
|
}
|
|
|
|
invariant MagicalWeaponCannotGiveHealth {
|
|
for w in MagicalWeapons, c in Characters:
|
|
not exists target: CharacterUsesWeapon(c, w, target) implies target.health <= target.health
|
|
}
|