Implement ReputationVector data model with per-entity multi-dimensional tracking #210

Open
opened 2026-03-16 01:36:38 +00:00 by freemo · 0 comments
Owner

Metadata

Field Value
Epic #201 — Reputation Data Model & Storage
Legendary #199 — Reputation & Social Standing
Type Feature
Priority High
Points 8
MoSCoW Must Have
Branch feature/m4-reputation-vector-data-model
Commit Message Implement ReputationVector data model with per-entity multi-dimensional tracking (#210)

Background and Context

The Reputation & Social Standing system requires a foundational data model to store and manage reputation scores between a player and any number of entities (factions, settlements, NPCs). The ReputationVector class serves as this foundation.

Reputation scores are stored as a hash of {entity_id => score} where each score is clamped to the range -100..+100. The class provides a clean API for reading, writing, and modifying reputation values. All mutations publish a Wisper event :reputation_changed so that other systems (propagation, UI, gameplay effects) can react to changes.

Data is persisted in player.info["reputation"] as a serializable hash structure. The class must handle missing or uninitialized reputation data gracefully, defaulting to 0 (Neutral) for unknown entities.

Expected Behavior

  1. ReputationVector.new(player) initializes from player.info["reputation"] or creates an empty hash if none exists.
  2. get_reputation(entity) returns the integer score for the given entity, defaulting to 0 if not found.
  3. set_reputation(entity, value) sets the score, clamping to -100..+100, saves to player.info["reputation"], and publishes :reputation_changed via Wisper with {player: player, entity: entity, old_score: old, new_score: new}.
  4. modify_reputation(entity, delta) adds delta to the current score, clamping the result, saves, and publishes the event.
  5. The data structure supports any number of entities per player.
  6. Score values are always integers clamped to -100..+100.

Acceptance Criteria

  • ReputationVector class exists in lib/aethyr/core/reputation/reputation_vector.rb.
  • get_reputation(entity) returns 0 for unknown entities and the stored score for known entities.
  • set_reputation(entity, value) clamps to -100..+100 and persists to player.info["reputation"].
  • modify_reputation(entity, delta) correctly adds delta and clamps.
  • Wisper event :reputation_changed is published on every set_reputation and modify_reputation call with correct payload.
  • Data round-trips correctly through save/load cycles.
  • Edge cases handled: nil entity, out-of-range values, concurrent modifications.

Subtasks

  • Create lib/aethyr/core/reputation/reputation_vector.rb with the ReputationVector class.
  • Implement initialize(player) loading from player.info["reputation"].
  • Implement get_reputation(entity) with default-to-zero behavior.
  • Implement set_reputation(entity, value) with clamping, persistence, and Wisper event.
  • Implement modify_reputation(entity, delta) with clamping, persistence, and Wisper event.
  • Add input validation and edge case handling.
  • Docs: Update YARD comments on affected classes and methods. Update relevant Docusaurus documentation pages if applicable.
  • Tests (Cucumber): Add tests/unit/reputation_vector.feature covering initialization, get/set/modify operations, clamping, default values, and Wisper event publishing.
  • Tests (Cucumber Integration): Add integration feature in tests/integration/ for reputation vector persistence and event handling.
  • Tests (Profiling): Run bundle exec rake unit_profile and verify no performance regressions.
  • Quality: Verify coverage >=97% via bundle exec rake unit. If coverage is <97% then review the current unit test coverage report at build/tests/unit/coverage/ and use it to write new Cucumber based unit tests to improve code coverage. Specifically, write Cucumber/Gherkin style unit tests that are descriptively named and specifically improve coverage on whichever file has the most uncovered lines by writing tests that will target the uncovered lines in the report. Once that is done rerun bundle exec rake unit to verify all tests pass and coverage is above >=97%. Only mark this as complete once coverage is >=97%, if not repeat this task as many times as is needed until coverage reaches >=97%.
  • Quality: Run bundle exec rake (default task: unit tests with coverage) and bundle exec rake integration, fix any errors if needed ensuring both pass across entire code base, do not ignore any failure even if it seems unrelated to this commit, fix it.

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
## Metadata | Field | Value | |-------|-------| | **Epic** | #201 — Reputation Data Model & Storage | | **Legendary** | #199 — Reputation & Social Standing | | **Type** | Feature | | **Priority** | High | | **Points** | 8 | | **MoSCoW** | Must Have | | **Branch** | `feature/m4-reputation-vector-data-model` | | **Commit Message** | `Implement ReputationVector data model with per-entity multi-dimensional tracking (#210)` | ## Background and Context The Reputation & Social Standing system requires a foundational data model to store and manage reputation scores between a player and any number of entities (factions, settlements, NPCs). The `ReputationVector` class serves as this foundation. Reputation scores are stored as a hash of `{entity_id => score}` where each score is clamped to the range `-100..+100`. The class provides a clean API for reading, writing, and modifying reputation values. All mutations publish a Wisper event `:reputation_changed` so that other systems (propagation, UI, gameplay effects) can react to changes. Data is persisted in `player.info["reputation"]` as a serializable hash structure. The class must handle missing or uninitialized reputation data gracefully, defaulting to 0 (Neutral) for unknown entities. ## Expected Behavior 1. `ReputationVector.new(player)` initializes from `player.info["reputation"]` or creates an empty hash if none exists. 2. `get_reputation(entity)` returns the integer score for the given entity, defaulting to 0 if not found. 3. `set_reputation(entity, value)` sets the score, clamping to `-100..+100`, saves to `player.info["reputation"]`, and publishes `:reputation_changed` via Wisper with `{player: player, entity: entity, old_score: old, new_score: new}`. 4. `modify_reputation(entity, delta)` adds `delta` to the current score, clamping the result, saves, and publishes the event. 5. The data structure supports any number of entities per player. 6. Score values are always integers clamped to `-100..+100`. ## Acceptance Criteria - [ ] `ReputationVector` class exists in `lib/aethyr/core/reputation/reputation_vector.rb`. - [ ] `get_reputation(entity)` returns 0 for unknown entities and the stored score for known entities. - [ ] `set_reputation(entity, value)` clamps to `-100..+100` and persists to `player.info["reputation"]`. - [ ] `modify_reputation(entity, delta)` correctly adds delta and clamps. - [ ] Wisper event `:reputation_changed` is published on every `set_reputation` and `modify_reputation` call with correct payload. - [ ] Data round-trips correctly through save/load cycles. - [ ] Edge cases handled: nil entity, out-of-range values, concurrent modifications. ## Subtasks - [ ] Create `lib/aethyr/core/reputation/reputation_vector.rb` with the `ReputationVector` class. - [ ] Implement `initialize(player)` loading from `player.info["reputation"]`. - [ ] Implement `get_reputation(entity)` with default-to-zero behavior. - [ ] Implement `set_reputation(entity, value)` with clamping, persistence, and Wisper event. - [ ] Implement `modify_reputation(entity, delta)` with clamping, persistence, and Wisper event. - [ ] Add input validation and edge case handling. - [ ] Docs: Update YARD comments on affected classes and methods. Update relevant Docusaurus documentation pages if applicable. - [ ] Tests (Cucumber): Add `tests/unit/reputation_vector.feature` covering initialization, get/set/modify operations, clamping, default values, and Wisper event publishing. - [ ] Tests (Cucumber Integration): Add integration feature in `tests/integration/` for reputation vector persistence and event handling. - [ ] Tests (Profiling): Run `bundle exec rake unit_profile` and verify no performance regressions. - [ ] Quality: Verify coverage >=97% via `bundle exec rake unit`. If coverage is <97% then review the current unit test coverage report at `build/tests/unit/coverage/` and use it to write new Cucumber based unit tests to improve code coverage. Specifically, write Cucumber/Gherkin style unit tests that are descriptively named and specifically improve coverage on whichever file has the most uncovered lines by writing tests that will target the uncovered lines in the report. Once that is done rerun `bundle exec rake unit` to verify all tests pass and coverage is above >=97%. Only mark this as complete once coverage is >=97%, if not repeat this task as many times as is needed until coverage reaches >=97%. - [ ] Quality: Run `bundle exec rake` (default task: unit tests with coverage) and `bundle exec rake integration`, fix any errors if needed ensuring both pass across **entire** code base, do not ignore any failure even if it seems unrelated to this commit, fix it. ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done.
freemo added this to the v1.3.0 milestone 2026-03-16 01:36:38 +00:00
freemo self-assigned this 2026-03-16 01:36:38 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference: aethyr/Aethyr#210
No description provided.