Skip to content

Commit

Permalink
Merge pull request #28 from Trisfald/status-effects
Browse files Browse the repository at this point in the history
Status effects
  • Loading branch information
Trisfald authored Mar 11, 2020
2 parents 4476835 + ad19a1f commit 7981d37
Show file tree
Hide file tree
Showing 38 changed files with 2,330 additions and 93 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ script:
- cargo fmt --all -- --check
- cargo clippy --tests --all-features -- -D warnings
- cargo test
- cargo test --all-features
- cargo clean
- CARGO_INCREMENTAL=0 RUSTFLAGS="-Ccodegen-units=1 -Cinline-threshold=0 -Coverflow-checks=off" cargo test --all-features

addons:
apt:
Expand All @@ -38,6 +39,6 @@ after_success: |
make install DESTDIR=../../kcov-build &&
cd ../.. &&
rm -rf kcov-master &&
for file in target/debug/*-*[^\.d]; do mkdir -p "target/cov/$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-path=./tests --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done &&
for file in target/debug/*-*; do if [ ${file: -2} == ".d" ]; then continue; fi; mkdir -p "target/cov/$(basename $file)"; ./kcov-build/usr/local/bin/kcov --exclude-path=./tests --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done &&
bash <(curl -s https://codecov.io/bash) &&
echo "Uploaded code coverage"
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Support for status effects.
- New methods `generate_status` and `alter_statuses` in `CharacterRules`.
- New methods `apply_status`, `update_status` and `delete_status` in `FightRules`.
- `InflictStatus` and `ClearStatus` events.
- Added `StatusNotPresent` to `WeaselError`.
- Mutable iterators over statistics and abilities.
- New event `EnvironmentRound`.
- New associated type `Potency` in `FightRules`.
- New associated types `Status` and `StatusesAlteration` in `CharacterRules`.
- Example to showcase status effects.

### Changed
- Renamed `ActorRules`'s `alter` into `alter_abilities` and `CharacterRules`'s `alter` into `alter_statistics`.

### Fixed
- Event's origin is not overridden anymore by the server if it is already set.

## [0.5.0] - 2020-02-26
### Added
Expand All @@ -25,11 +42,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Doc tests for all events and few other structs.
- `Originated` decorator.
- Introduced inanimate objects.
- New events `CreateObject` and `RemoveObject`.
- Improved public API for `Battle` and its submodules.
- New associated type `ObjectId` in `CharacterRules`.

### Changed
- It's now possible to manually set an event's origin.
- New associated type `ObjectId` in `CharacterRules`.

## [0.3.1] - 2020-02-17
### Added
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ name = "undo"

[[example]]
name = "passive"

[[example]]
name = "status"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ weasel provides many functionalities to ease the development of a turn based gam

- Creatures and inanimate objects.
- Statistics and abilities for characters.
- Long lasting status effects.
- Player managed teams.
- Team objectives and diplomacy.
- Division of the battle into rounds.
Expand Down
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ Interactive example in which the player moves around a creature and can undo/red
## [Passive](passive/)

An example to show how to define simple passive abilities.

## [Status](status/)

Example to demonstrate the usage of status effects and how to write rules for them.
28 changes: 14 additions & 14 deletions examples/initiative/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Initiative

This example shows how to implement `RoundsRules` to decide the order of acting during a battle.

The first step is to create five creatures, each one with a different value of *speed*. Then, we will repeatedly start and end rounds while also displaying the global order of initiative.

Run the example with:
```
cargo run --example initiative --all-features
```

The program is implemented in two source code files:
- [rules.rs](rules.rs): rules definition (round rules, in particular).
- [main.rs](main.rs): manages the battle and creates a few events.
# Initiative

This example shows how to implement `RoundsRules` to decide the order of acting during a battle.

The first step is to create five creatures, each one with a different value of *speed*. Then, we will repeatedly start and end rounds while also displaying the global order of initiative.

Run the example with:
```
cargo run --example initiative --all-features
```

The program is implemented in two source code files:
- [rules.rs](rules.rs): rules definition (round rules, in particular).
- [main.rs](main.rs): manages the battle and creates a few events.
3 changes: 3 additions & 0 deletions examples/initiative/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ impl CharacterRules<CustomRules> for CustomCharacterRules {
type StatisticsSeed = u16;
// We never alter statistics in this example.
type StatisticsAlteration = ();
// No status effects in this game.
type Status = EmptyStatus;
type StatusesAlteration = ();

fn generate_statistics(
&self,
Expand Down
2 changes: 1 addition & 1 deletion examples/passive/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl ActorRules<CustomRules> for CustomActorRules {
Box::new(v.into_iter())
}

fn alter(
fn alter_abilities(
&self,
actor: &mut dyn Actor<CustomRules>,
alteration: &Self::AbilitiesAlteration,
Expand Down
7 changes: 6 additions & 1 deletion examples/pirates/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ impl CharacterRules<PiratesRules> for PiratesCharacterRules {
type StatisticsSeed = ();
// Our alteration for statistics consists of the values to add to HULL and to CREW.
type StatisticsAlteration = (i16, i16);
// This game doesn't have long lasting status effects.
type Status = EmptyStatus;
type StatusesAlteration = ();

// In this method we generate statistics of ships.
fn generate_statistics(
Expand All @@ -98,7 +101,7 @@ impl CharacterRules<PiratesRules> for PiratesCharacterRules {
}

// Method to alter the statistics of ships. In this case we want to decrease hull and crew.
fn alter(
fn alter_statistics(
&self,
character: &mut dyn Character<PiratesRules>,
alteration: &Self::StatisticsAlteration,
Expand Down Expand Up @@ -190,6 +193,8 @@ pub struct PiratesFightRules {}
impl FightRules<PiratesRules> for PiratesFightRules {
// Our impact type will be a tuple with target id and a statistics alteration.
type Impact = (EntityId<PiratesRules>, StatisticsAlteration<PiratesRules>);
// There are no status effects in this game, so no need to define potency.
type Potency = ();

fn apply_impact(
&self,
Expand Down
36 changes: 18 additions & 18 deletions examples/space/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Space

In this example we'll discover how to manage the *space dimension* in weasel.

Our space model will start as a two dimensional plane, divided in squares. We will then spawn a few creatures, each one on a different square.

As the next step, deadly traps will be placed across the two diagonals.

Finally, we are going to regenerate the space, transforming the 2D plane into a single line of squares; in other words we drop one dimension.

Run the example with:
```
cargo run --example space
```

The program is implemented in two source code files:
- [rules.rs](rules.rs): rules definition (space rules, in particular).
- [main.rs](main.rs): manages the battle and creates a few events.
# Space

In this example we'll discover how to manage the *space dimension* in weasel.

Our space model will start as a two dimensional plane, divided in squares. We will then spawn a few creatures, each one on a different square.

As the next step, deadly traps will be placed across the two diagonals.

Finally, we are going to regenerate the space, transforming the 2D plane into a single line of squares; in other words we drop one dimension.

Run the example with:
```
cargo run --example space
```

The program is implemented in two source code files:
- [rules.rs](rules.rs): rules definition (space rules, in particular).
- [main.rs](main.rs): manages the battle and creates a few events.
14 changes: 14 additions & 0 deletions examples/status/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Status

In this example we will demonstrate how to implement different types of long lasting status effects.

We will first create a creature and an object. Then, we inflict a status effect on the creature that will increase its health as long as it is active. The object, instead, will be dealt damage over time which will reduce its life at each round. Finally, we will see how to end the effects manually or after a certain number of rounds.

Run the example with:
```
cargo run --example status
```

The program is implemented in two source code files:
- [rules.rs](rules.rs): rules definition.
- [main.rs](main.rs): manages the displayed messages and handles the battle server.
126 changes: 126 additions & 0 deletions examples/status/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use crate::rules::*;
use weasel::battle::Battle;
use weasel::creature::{CreateCreature, CreatureId};
use weasel::entity::EntityId;
use weasel::event::{EventKind, EventTrigger};
use weasel::object::{CreateObject, ObjectId};
use weasel::round::{EndRound, EnvironmentRound, StartRound};
use weasel::status::{ClearStatus, InflictStatus};
use weasel::team::{CreateTeam, TeamId};
use weasel::util::Id;
use weasel::Server;

mod rules;

static TEAM_ID: TeamId<CustomRules> = 1;
static CREATURE_ID: CreatureId<CustomRules> = 1;
static OBJECT_ID: ObjectId<CustomRules> = 2;
static ENTITY_1_ID: EntityId<CustomRules> = EntityId::Creature(CREATURE_ID);
static ENTITY_2_ID: EntityId<CustomRules> = EntityId::Object(OBJECT_ID);

fn main() {
// Create a server to manage the battle.
let battle = Battle::builder(CustomRules::new()).build();
let mut server = Server::builder(battle).build();
// Create a team.
CreateTeam::trigger(&mut server, TEAM_ID).fire().unwrap();
// Spawn a creature and an object, both with 50 HEALTH.
println!("Spawning a creature...");
CreateCreature::trigger(&mut server, CREATURE_ID, TEAM_ID, ())
.statistics_seed(50)
.fire()
.unwrap();
println!("Spawning an object...");
CreateObject::trigger(&mut server, OBJECT_ID, ())
.statistics_seed(50)
.fire()
.unwrap();
// Display the entities' state.
print_state(&server);
// Inflict a power-up status effect on the creature, with no time limit.
println!("Inflicting a power-up on the creature...");
InflictStatus::trigger(&mut server, ENTITY_1_ID, VIGOR)
.potency((50, None))
.fire()
.unwrap();
// Inflict a DoT status effect on the object, for two rounds.
println!("Inflicting a DoT on the object...");
InflictStatus::trigger(&mut server, ENTITY_2_ID, DOT)
.potency((10, Some(2)))
.fire()
.unwrap();
// Display the entities' state.
print_state(&server);
// Do two full turns.
for i in 1..=2 {
turn(&mut server, i);
}
// The DoT should have been cleared automatically.
// Remove the power-up manually.
println!("Removing the creature power-up...");
ClearStatus::trigger(&mut server, ENTITY_1_ID, VIGOR)
.fire()
.unwrap();
print_state(&server);
// Display the link between the DoT status and the effects it created.
print_dot_effects(&server);
}

/// Performs a turn.
fn turn(server: &mut Server<CustomRules>, turn: u32) {
// Display in which turn we are.
println!("Turn {}", turn);
println!();
// Start and end a round for the creature.
println!("Round of Creature (1)...");
StartRound::trigger(server, ENTITY_1_ID).fire().unwrap();
EndRound::trigger(server).fire().unwrap();
// Do a round for all non-actor entities, to update their statuses.
println!("Round of environment...");
EnvironmentRound::trigger(server).fire().unwrap();
// Display the entities' state.
print_state(server);
}

/// Displays briefly the state of all entities.
fn print_state(server: &Server<CustomRules>) {
println!();
println!("------------------------- Entities -------------------------");
for character in server.battle().entities().characters() {
let statuses: Vec<_> = character
.statuses()
.map(|status| match *status.id() {
VIGOR => "vigor",
DOT => "DoT",
_ => unimplemented!(),
})
.collect();
println!(
"{:?} => health: {}, statuses: {:?}",
character.entity_id(),
character.statistic(&HEALTH).unwrap().value(),
statuses
);
}
println!();
}

fn print_dot_effects(server: &Server<CustomRules>) {
println!("Event derived from the DOT status:");
// We want to show the chain of events derived from the DOT status.
// First find the event that put the DOT on the object.
// We know it's first InflictStatus iterating in reverse order.
let events = server.battle().history().events();
let inflict_event = events
.iter()
.rev()
.find(|e| e.kind() == EventKind::InflictStatus)
.unwrap();
println!("{:?}", inflict_event.event());
// Get all events with inflict_event as origin.
for event in events {
if event.origin() == Some(inflict_event.id()) {
println!("+-- {:?}", event.event());
}
}
}
Loading

0 comments on commit 7981d37

Please sign in to comment.