Preserve materialized Gestalt actor authority
This commit is contained in:
parent
72c3dced2d
commit
d77f94f49c
4 changed files with 368 additions and 62 deletions
|
|
@ -2568,13 +2568,6 @@ fn strategic_activity_summary(
|
|||
}
|
||||
|
||||
fn agency_subject_name(campaign: &Campaign, subject_id: &str) -> Result<String, KernelError> {
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
return campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.map(|value| value.name.clone())
|
||||
.ok_or_else(|| KernelError::Invalid("strategic activity target vanished".into()));
|
||||
}
|
||||
campaign
|
||||
.actors
|
||||
.get(subject_id)
|
||||
|
|
@ -2591,6 +2584,16 @@ fn agency_subject_name(campaign: &Campaign, subject_id: &str) -> Result<String,
|
|||
.get(subject_id)
|
||||
.map(|value| value.name.clone())
|
||||
})
|
||||
.or_else(|| {
|
||||
crate::resolution::dormant_member_id_for_subject(campaign, subject_id).and_then(
|
||||
|member_id| {
|
||||
campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.map(|value| value.name.clone())
|
||||
},
|
||||
)
|
||||
})
|
||||
.ok_or_else(|| KernelError::Invalid("strategic activity target vanished".into()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1165,10 +1165,11 @@ fn action_context(
|
|||
resource_owner_id: proposal.subject_id.clone(),
|
||||
resource_recipient_ids,
|
||||
discoverable_facts,
|
||||
member_state_owner_id: proposal
|
||||
.subject_id
|
||||
.strip_prefix("member:")
|
||||
.map(str::to_owned),
|
||||
member_state_owner_id: crate::resolution::dormant_member_id_for_subject(
|
||||
campaign,
|
||||
&proposal.subject_id,
|
||||
)
|
||||
.map(str::to_owned),
|
||||
admissible_effect_kinds: admissible_effect_kinds(campaign, proposal)?,
|
||||
allowed_state_references: allowed_state_references(campaign, proposal)?
|
||||
.into_iter()
|
||||
|
|
@ -1215,7 +1216,7 @@ fn admissible_effect_kinds(
|
|||
if !pressure_owner_ids(campaign, proposal)?.is_empty() {
|
||||
kinds.push(OutcomeEffectKind::GestaltPressure);
|
||||
}
|
||||
if !source.starts_with("member:")
|
||||
if crate::resolution::dormant_member_id_for_subject(campaign, source).is_none()
|
||||
&& matches!(
|
||||
activity,
|
||||
StrategicActivityKind::Coordinate
|
||||
|
|
@ -1234,7 +1235,7 @@ fn admissible_effect_kinds(
|
|||
{
|
||||
kinds.push(OutcomeEffectKind::AgencyRelationShift);
|
||||
}
|
||||
if let Some(member_id) = source.strip_prefix("member:") {
|
||||
if let Some(member_id) = crate::resolution::dormant_member_id_for_subject(campaign, source) {
|
||||
let member = &campaign.gestalt_members[member_id];
|
||||
if member.memories.len() < 64 {
|
||||
kinds.push(OutcomeEffectKind::MemberMemory);
|
||||
|
|
@ -1265,7 +1266,16 @@ fn admissible_effect_kinds(
|
|||
}
|
||||
|
||||
fn subject_summary(campaign: &Campaign, subject_id: &str) -> Result<serde_json::Value> {
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
if let Some(actor) = campaign.actors.get(subject_id) {
|
||||
return Ok(serde_json::json!({
|
||||
"subject_id":subject_id,"name":actor.name,"kind":"actor",
|
||||
"capabilities":actor.capabilities,"knowledge":actor.knowledge,
|
||||
"resources":actor.equipment,"conditions":actor.conditions,
|
||||
"obligations":actor.obligations,"relationships":actor.relationships,"goals":actor.goals,
|
||||
}));
|
||||
}
|
||||
if let Some(member_id) = crate::resolution::dormant_member_id_for_subject(campaign, subject_id)
|
||||
{
|
||||
let member = campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
|
|
@ -1297,14 +1307,6 @@ fn subject_summary(campaign: &Campaign, subject_id: &str) -> Result<serde_json::
|
|||
"resources":institution.resources,"goals":institution.goals,"posture":institution.posture,
|
||||
}));
|
||||
}
|
||||
if let Some(actor) = campaign.actors.get(subject_id) {
|
||||
return Ok(serde_json::json!({
|
||||
"subject_id":subject_id,"name":actor.name,"kind":"actor",
|
||||
"capabilities":actor.capabilities,"knowledge":actor.knowledge,
|
||||
"resources":actor.equipment,"conditions":actor.conditions,
|
||||
"obligations":actor.obligations,"relationships":actor.relationships,"goals":actor.goals,
|
||||
}));
|
||||
}
|
||||
Err(anyhow!("outcome context subject vanished"))
|
||||
}
|
||||
|
||||
|
|
@ -1312,7 +1314,9 @@ fn allowed_state_references(
|
|||
campaign: &Campaign,
|
||||
proposal: &CellActionProposal,
|
||||
) -> Result<BTreeSet<String>> {
|
||||
let mut references = if let Some(member_id) = proposal.subject_id.strip_prefix("member:") {
|
||||
let mut references = if let Some(member_id) =
|
||||
crate::resolution::dormant_member_id_for_subject(campaign, &proposal.subject_id)
|
||||
{
|
||||
crate::resolution::member_state_references(campaign, member_id)?
|
||||
} else {
|
||||
subject_state_references(campaign, &proposal.subject_id)?
|
||||
|
|
@ -1396,20 +1400,25 @@ fn discoverable_fact_ids(
|
|||
}
|
||||
|
||||
fn source_knowledge(campaign: &Campaign, subject_id: &str) -> Result<BTreeSet<String>> {
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
return effective_member_knowledge(campaign, member_id);
|
||||
}
|
||||
if let Some(gestalt) = campaign.gestalts.get(subject_id) {
|
||||
return Ok(gestalt.shared_knowledge.clone());
|
||||
}
|
||||
if let Some(actor) = campaign.actors.get(subject_id) {
|
||||
return Ok(actor.knowledge.clone());
|
||||
}
|
||||
if let Some(member_id) = crate::resolution::dormant_member_id_for_subject(campaign, subject_id)
|
||||
{
|
||||
return effective_member_knowledge(campaign, member_id);
|
||||
}
|
||||
Ok(BTreeSet::new())
|
||||
}
|
||||
|
||||
pub fn subject_resources(campaign: &Campaign, subject_id: &str) -> Result<BTreeSet<String>> {
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
if let Some(actor) = campaign.actors.get(subject_id) {
|
||||
return Ok(actor.equipment.clone());
|
||||
}
|
||||
if let Some(member_id) = crate::resolution::dormant_member_id_for_subject(campaign, subject_id)
|
||||
{
|
||||
return campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
|
|
@ -1422,9 +1431,6 @@ pub fn subject_resources(campaign: &Campaign, subject_id: &str) -> Result<BTreeS
|
|||
if let Some(institution) = campaign.institutions.get(subject_id) {
|
||||
return Ok(institution.resources.iter().cloned().collect());
|
||||
}
|
||||
if let Some(actor) = campaign.actors.get(subject_id) {
|
||||
return Ok(actor.equipment.clone());
|
||||
}
|
||||
Err(anyhow!("resource owner is unknown"))
|
||||
}
|
||||
|
||||
|
|
@ -1478,13 +1484,6 @@ fn activity_parts(
|
|||
}
|
||||
|
||||
fn subject_name(campaign: &Campaign, subject_id: &str) -> Result<String> {
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
return campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.map(|member| member.name.clone())
|
||||
.ok_or_else(|| anyhow!("outcome source member vanished"));
|
||||
}
|
||||
campaign
|
||||
.actors
|
||||
.get(subject_id)
|
||||
|
|
@ -1501,6 +1500,16 @@ fn subject_name(campaign: &Campaign, subject_id: &str) -> Result<String> {
|
|||
.get(subject_id)
|
||||
.map(|value| value.name.clone())
|
||||
})
|
||||
.or_else(|| {
|
||||
crate::resolution::dormant_member_id_for_subject(campaign, subject_id).and_then(
|
||||
|member_id| {
|
||||
campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.map(|value| value.name.clone())
|
||||
},
|
||||
)
|
||||
})
|
||||
.ok_or_else(|| anyhow!("outcome source vanished"))
|
||||
}
|
||||
|
||||
|
|
@ -1511,8 +1520,7 @@ fn outcome_source_receipts(campaign: &Campaign, proposal: &CellActionProposal) -
|
|||
subjects.extend(targets);
|
||||
}
|
||||
for subject in subjects {
|
||||
let profile_id = subject
|
||||
.strip_prefix("member:")
|
||||
let profile_id = crate::resolution::dormant_member_id_for_subject(campaign, &subject)
|
||||
.and_then(|member_id| campaign.gestalt_members.get(member_id))
|
||||
.map(|member| member.gestalt_id.as_str())
|
||||
.unwrap_or(&subject);
|
||||
|
|
@ -2306,6 +2314,96 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn materialized_member_outcome_context_uses_actor_state_and_effects() {
|
||||
let mut value = campaign();
|
||||
value.gestalt_members.insert(
|
||||
"sable".into(),
|
||||
crate::domain::GestaltMemberDelta {
|
||||
schema: "ghostlight.gestalt_member_delta.v1".into(),
|
||||
id: "sable".into(),
|
||||
gestalt_id: "dockers".into(),
|
||||
version: 1,
|
||||
name: "Sable".into(),
|
||||
capability_additions: BTreeSet::new(),
|
||||
capability_removals: BTreeSet::new(),
|
||||
knowledge_additions: BTreeSet::new(),
|
||||
knowledge_removals: BTreeSet::new(),
|
||||
equipment: BTreeSet::from(["folded kit".into()]),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
last_location_id: Some("dock".into()),
|
||||
materialized_actor_id: Some("member:sable".into()),
|
||||
last_relevant_revision: 4,
|
||||
relevance_lease_until_revision: 9,
|
||||
},
|
||||
);
|
||||
value.actors.insert(
|
||||
"member:sable".into(),
|
||||
crate::domain::ActorState {
|
||||
id: "member:sable".into(),
|
||||
name: "Sable".into(),
|
||||
location_id: "dock".into(),
|
||||
capabilities: BTreeSet::from(["route scouting".into()]),
|
||||
knowledge: BTreeSet::from(["the east gate is watched".into()]),
|
||||
equipment: BTreeSet::from(["materialized kit".into()]),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
},
|
||||
);
|
||||
ensure_agency_profiles(&mut value);
|
||||
let action = CellActionProposal {
|
||||
subject_id: "member:sable".into(),
|
||||
intent: "inspect the eastern trail".into(),
|
||||
intended_effect: "identify an immediate route hazard".into(),
|
||||
priority: 50,
|
||||
state_references: vec![
|
||||
"subject:member:sable".into(),
|
||||
"location:dock".into(),
|
||||
"capability:route scouting".into(),
|
||||
],
|
||||
public_channels: vec![],
|
||||
effect: StrategicCellEffect::ActorActivity {
|
||||
actor_id: "member:sable".into(),
|
||||
activity: StrategicActivityKind::Investigate,
|
||||
target_subject_ids: vec![],
|
||||
location_ids: vec!["dock".into()],
|
||||
},
|
||||
};
|
||||
|
||||
let context = build_context(&value, &[action]).unwrap();
|
||||
let action = &context.actions[0];
|
||||
assert_eq!(action.source_state["kind"], "actor");
|
||||
assert_eq!(action.source_state["resources"][0], "materialized kit");
|
||||
assert_eq!(action.member_state_owner_id, None);
|
||||
assert!(
|
||||
action
|
||||
.allowed_state_references
|
||||
.contains(&"subject:member:sable".into())
|
||||
);
|
||||
assert!(
|
||||
!action
|
||||
.admissible_effect_kinds
|
||||
.contains(&OutcomeEffectKind::MemberMemory)
|
||||
);
|
||||
assert!(
|
||||
!action
|
||||
.admissible_effect_kinds
|
||||
.contains(&OutcomeEffectKind::MemberObligation)
|
||||
);
|
||||
assert!(
|
||||
!action
|
||||
.admissible_effect_kinds
|
||||
.contains(&OutcomeEffectKind::MemberRelationship)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn knowledge_outcome_requires_exact_discoverable_fact() {
|
||||
let value = campaign();
|
||||
|
|
|
|||
|
|
@ -1848,10 +1848,10 @@ fn validate_cell_proposal(
|
|||
{
|
||||
return Err(anyhow!("cell proposal has no exact constituent authority"));
|
||||
}
|
||||
if let Some(member_id) = proposal.subject_id.strip_prefix("member:") {
|
||||
return validate_member_cell_proposal(campaign, cell, member_id, proposal);
|
||||
}
|
||||
if !cell.subject_ids.contains(&proposal.subject_id) {
|
||||
if let Some(member_id) = dormant_member_id_for_subject(campaign, &proposal.subject_id) {
|
||||
return validate_member_cell_proposal(campaign, cell, member_id, proposal);
|
||||
}
|
||||
return Err(anyhow!("cell proposal has no exact constituent authority"));
|
||||
}
|
||||
let profile = campaign
|
||||
|
|
@ -2026,13 +2026,15 @@ fn cell_contains_attributed_subject(
|
|||
if is_human_controlled_actor(campaign, subject_id) {
|
||||
return false;
|
||||
}
|
||||
if let Some(member_id) = subject_id.strip_prefix("member:") {
|
||||
return campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.is_some_and(|member| cell.subject_ids.contains(&member.gestalt_id));
|
||||
if cell.subject_ids.contains(subject_id) {
|
||||
return true;
|
||||
}
|
||||
cell.subject_ids.contains(subject_id)
|
||||
if let Some(member_id) = dormant_member_id_for_subject(campaign, subject_id) {
|
||||
return cell
|
||||
.subject_ids
|
||||
.contains(&campaign.gestalt_members[member_id].gestalt_id);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_human_controlled_actor(campaign: &Campaign, subject_id: &str) -> bool {
|
||||
|
|
@ -2625,6 +2627,23 @@ pub fn member_activity_targets(campaign: &Campaign, member_id: &str) -> Result<B
|
|||
Ok(targets)
|
||||
}
|
||||
|
||||
/// Resolve a subject-shaped member ID only while its canonical person remains
|
||||
/// folded into a Gestalt. Materialized people deliberately retain the same
|
||||
/// stable ID, so the `member:` prefix cannot own their current subject kind.
|
||||
pub fn dormant_member_id_for_subject<'a>(
|
||||
campaign: &'a Campaign,
|
||||
subject_id: &'a str,
|
||||
) -> Option<&'a str> {
|
||||
let member_id = subject_id.strip_prefix("member:")?;
|
||||
campaign
|
||||
.gestalt_members
|
||||
.get(member_id)
|
||||
.filter(|member| {
|
||||
member.materialized_actor_id.is_none() && !campaign.actors.contains_key(subject_id)
|
||||
})
|
||||
.map(|_| member_id)
|
||||
}
|
||||
|
||||
pub fn validate_active_gestalt_presence_location(
|
||||
campaign: &Campaign,
|
||||
gestalt_id: &str,
|
||||
|
|
@ -2978,6 +2997,120 @@ pub(crate) mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn materialized_member_actor_has_one_exact_cell_authority() {
|
||||
let mut value = campaign(0, 2);
|
||||
value.gestalts.insert(
|
||||
"refugees".into(),
|
||||
GestaltPersonaState {
|
||||
schema: "ghostlight.gestalt_persona_state.v1".into(),
|
||||
id: "refugees".into(),
|
||||
name: "Refugees".into(),
|
||||
version: 0,
|
||||
home_location_id: "center".into(),
|
||||
shared_capabilities: BTreeSet::new(),
|
||||
shared_knowledge: BTreeSet::new(),
|
||||
resources: BTreeSet::new(),
|
||||
goals: vec![],
|
||||
pressures: vec![],
|
||||
},
|
||||
);
|
||||
value.gestalt_members.insert(
|
||||
"sable".into(),
|
||||
GestaltMemberDelta {
|
||||
schema: "ghostlight.gestalt_member_delta.v1".into(),
|
||||
id: "sable".into(),
|
||||
gestalt_id: "refugees".into(),
|
||||
version: 1,
|
||||
name: "Sable".into(),
|
||||
capability_additions: BTreeSet::new(),
|
||||
capability_removals: BTreeSet::new(),
|
||||
knowledge_additions: BTreeSet::new(),
|
||||
knowledge_removals: BTreeSet::new(),
|
||||
equipment: BTreeSet::new(),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
last_location_id: Some("center".into()),
|
||||
materialized_actor_id: Some("member:sable".into()),
|
||||
last_relevant_revision: 0,
|
||||
relevance_lease_until_revision: 5,
|
||||
},
|
||||
);
|
||||
value.actors.insert(
|
||||
"member:sable".into(),
|
||||
ActorState {
|
||||
id: "member:sable".into(),
|
||||
name: "Sable".into(),
|
||||
location_id: "center".into(),
|
||||
capabilities: BTreeSet::from(["route scouting".into()]),
|
||||
knowledge: BTreeSet::new(),
|
||||
equipment: BTreeSet::new(),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
},
|
||||
);
|
||||
ensure_agency_profiles(&mut value);
|
||||
let actor_cell = SimulationCell {
|
||||
schema: "ghostlight.simulation_cell.v1".into(),
|
||||
id: "actor-cell".into(),
|
||||
mode: SimulationCellMode::Arena,
|
||||
subject_ids: BTreeSet::from(["member:sable".into()]),
|
||||
merge_loss: MergeLoss::default(),
|
||||
rationale: "exact materialized actor".into(),
|
||||
lease_until_world_revision: 0,
|
||||
lease_until_strategic_tick: 0,
|
||||
detail_focus_subject_id: None,
|
||||
};
|
||||
let population_cell = SimulationCell {
|
||||
schema: "ghostlight.simulation_cell.v1".into(),
|
||||
id: "population-cell".into(),
|
||||
mode: SimulationCellMode::Cohesive,
|
||||
subject_ids: BTreeSet::from(["refugees".into()]),
|
||||
merge_loss: MergeLoss::default(),
|
||||
rationale: "population baseline".into(),
|
||||
lease_until_world_revision: 0,
|
||||
lease_until_strategic_tick: 0,
|
||||
detail_focus_subject_id: None,
|
||||
};
|
||||
let proposal = CellActionProposal {
|
||||
subject_id: "member:sable".into(),
|
||||
intent: "inspect the eastern trail".into(),
|
||||
intended_effect: "identify an immediate route hazard".into(),
|
||||
priority: 50,
|
||||
state_references: vec![
|
||||
"subject:member:sable".into(),
|
||||
"location:center".into(),
|
||||
"capability:route scouting".into(),
|
||||
],
|
||||
public_channels: vec![],
|
||||
effect: StrategicCellEffect::ActorActivity {
|
||||
actor_id: "member:sable".into(),
|
||||
activity: StrategicActivityKind::Investigate,
|
||||
target_subject_ids: vec![],
|
||||
location_ids: vec!["center".into()],
|
||||
},
|
||||
};
|
||||
|
||||
validate_cell_proposal(&value, &actor_cell, &proposal).unwrap();
|
||||
assert!(cell_contains_attributed_subject(
|
||||
&value,
|
||||
&actor_cell,
|
||||
"member:sable"
|
||||
));
|
||||
assert!(!cell_contains_attributed_subject(
|
||||
&value,
|
||||
&population_cell,
|
||||
"member:sable"
|
||||
));
|
||||
assert_eq!(dormant_member_id_for_subject(&value, "member:sable"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aetheria_scale_cover_is_complete_unique_and_budgeted() {
|
||||
for budget in [1, 4, 8, 32] {
|
||||
|
|
|
|||
|
|
@ -837,20 +837,7 @@ fn activity_target_slices(
|
|||
.iter()
|
||||
.map(|target_id| {
|
||||
let (name, location_ids) =
|
||||
if let Some(member_id) = target_id.strip_prefix("member:") {
|
||||
let member = campaign.gestalt_members.get(member_id).ok_or_else(|| {
|
||||
anyhow!("activity target {target_id} has no member state")
|
||||
})?;
|
||||
(
|
||||
member.name.clone(),
|
||||
BTreeSet::from([crate::resolution::dormant_member_location(
|
||||
campaign, member_id,
|
||||
)?]),
|
||||
)
|
||||
} else {
|
||||
let profile = campaign.agency_profiles.get(target_id).ok_or_else(|| {
|
||||
anyhow!("activity target {target_id} has no agency profile")
|
||||
})?;
|
||||
if let Some(profile) = campaign.agency_profiles.get(target_id) {
|
||||
let name = campaign
|
||||
.actors
|
||||
.get(target_id)
|
||||
|
|
@ -871,6 +858,20 @@ fn activity_target_slices(
|
|||
anyhow!("activity target {target_id} has no canonical named subject")
|
||||
})?;
|
||||
(name, profile.location_ids.clone())
|
||||
} else if let Some(member_id) =
|
||||
crate::resolution::dormant_member_id_for_subject(campaign, target_id)
|
||||
{
|
||||
let member = campaign.gestalt_members.get(member_id).ok_or_else(|| {
|
||||
anyhow!("activity target {target_id} has no member state")
|
||||
})?;
|
||||
(
|
||||
member.name.clone(),
|
||||
BTreeSet::from([crate::resolution::dormant_member_location(
|
||||
campaign, member_id,
|
||||
)?]),
|
||||
)
|
||||
} else {
|
||||
return Err(anyhow!("activity target {target_id} has no agency profile"));
|
||||
};
|
||||
let locations = location_ids
|
||||
.into_iter()
|
||||
|
|
@ -1182,6 +1183,77 @@ mod tests {
|
|||
assert!(!reed.locations.contains_key("garrison"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn materialized_gestalt_member_is_projected_as_an_actor_target() {
|
||||
use crate::domain::{ActorState, GestaltMemberDelta, GestaltPersonaState};
|
||||
|
||||
let mut campaign = crate::resolution::tests::campaign(0, 2);
|
||||
campaign.gestalts.insert(
|
||||
"refugees".into(),
|
||||
GestaltPersonaState {
|
||||
schema: "ghostlight.gestalt_persona_state.v1".into(),
|
||||
id: "refugees".into(),
|
||||
name: "Refugees".into(),
|
||||
version: 0,
|
||||
home_location_id: "center".into(),
|
||||
shared_capabilities: BTreeSet::new(),
|
||||
shared_knowledge: BTreeSet::new(),
|
||||
resources: BTreeSet::new(),
|
||||
goals: vec![],
|
||||
pressures: vec![],
|
||||
},
|
||||
);
|
||||
campaign.gestalt_members.insert(
|
||||
"sable".into(),
|
||||
GestaltMemberDelta {
|
||||
schema: "ghostlight.gestalt_member_delta.v1".into(),
|
||||
id: "sable".into(),
|
||||
gestalt_id: "refugees".into(),
|
||||
version: 1,
|
||||
name: "Sable".into(),
|
||||
capability_additions: BTreeSet::new(),
|
||||
capability_removals: BTreeSet::new(),
|
||||
knowledge_additions: BTreeSet::new(),
|
||||
knowledge_removals: BTreeSet::new(),
|
||||
equipment: BTreeSet::new(),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
last_location_id: Some("center".into()),
|
||||
materialized_actor_id: Some("member:sable".into()),
|
||||
last_relevant_revision: 0,
|
||||
relevance_lease_until_revision: 5,
|
||||
},
|
||||
);
|
||||
campaign.actors.insert(
|
||||
"member:sable".into(),
|
||||
ActorState {
|
||||
id: "member:sable".into(),
|
||||
name: "Sable".into(),
|
||||
location_id: "center".into(),
|
||||
capabilities: BTreeSet::from(["route scouting".into()]),
|
||||
knowledge: BTreeSet::new(),
|
||||
equipment: BTreeSet::new(),
|
||||
conditions: BTreeSet::new(),
|
||||
obligations: BTreeSet::new(),
|
||||
relationships: BTreeMap::new(),
|
||||
goals: vec![],
|
||||
memories: vec![],
|
||||
},
|
||||
);
|
||||
crate::resolution::ensure_agency_profiles(&mut campaign);
|
||||
|
||||
let slice = constituent_slice(&campaign, "player").unwrap();
|
||||
let sable = slice.activity_targets.get("member:sable").unwrap();
|
||||
assert_eq!(sable.name, "Sable");
|
||||
assert_eq!(
|
||||
sable.locations.get("center").map(String::as_str),
|
||||
Some("Center")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cell_slice_projects_only_actionable_salient_member_exceptions() {
|
||||
use crate::domain::*;
|
||||
|
|
|
|||
Loading…
Reference in a new issue