Skip to content

Commit

Permalink
Merge pull request #36 from AndyTechGuy/characterFactions
Browse files Browse the repository at this point in the history
Implementation of a Character Faction System
  • Loading branch information
AndyTechGuy committed May 9, 2020
2 parents 074ea0f + 3e0398f commit 3b61ae0
Show file tree
Hide file tree
Showing 13 changed files with 394 additions and 33 deletions.
16 changes: 16 additions & 0 deletions assets/behaviors/citizen.behavior
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
{
dynamic: [
{
guard: {
componentPresent: "MetalRenegades:NearbyCitizenEnemies",
values: ["N enemiesWithinRange nonEmpty"],
child: {
sequence: [
set_target_to_enemy,
{
lookup: {
tree: "Behaviors:hostile"
}
}
]
}
}
},
{
selector: [
{
Expand Down
5 changes: 4 additions & 1 deletion assets/prefabs/characters/badCitizen.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@
"Behavior": {
"tree": "MetalRenegades:citizen"
},
"Trader": {}
"Trader": {},
"FactionAlignment": {
"alignment": "BAD"
}
}
8 changes: 7 additions & 1 deletion assets/prefabs/characters/baseCitizen.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
},
"Network": {},
"MinionMove": {},
"Health": {},
"Health": {
"destroyEntityOnNoHealth" : true,
"currentHealth": 30,
"maxHealth": 30
},
"BoxShape": {
"extents": [
1.0,
Expand All @@ -63,6 +67,8 @@
]
},
"Citizen": {},
"NearbyCitizenEnemies": {},
"AttackOnHit": {},
"Inventory": {
"privateToOwner": false,
"itemSlots": [
Expand Down
5 changes: 4 additions & 1 deletion assets/prefabs/characters/goodCitizen.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@
"Behavior": {
"tree": "MetalRenegades:citizen"
},
"Trader": {}
"Trader": {},
"FactionAlignment": {
"alignment": "GOOD"
}
}
5 changes: 4 additions & 1 deletion assets/prefabs/characters/gooeyCitizen.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@
"Behavior": {
"tree": "MetalRenegades:citizen"
},
"Trader": {}
"Trader": {},
"FactionAlignment": {
"alignment": "NEUTRAL"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public class CheckNeedAction extends BaseAction {

@Override
public BehaviorState modify(Actor actor, BehaviorState result) {
if(!actor.hasComponent(NeedsComponent.class)) {
return BehaviorState.FAILURE;
}

CitizenNeed.Type needTypeValue = CitizenNeed.Type.valueOf(needType);

NeedsComponent needsComponent = actor.getComponent(NeedsComponent.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2018 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.metalrenegades.ai.actions;

import org.terasology.behaviors.components.FollowComponent;
import org.terasology.logic.behavior.BehaviorAction;
import org.terasology.logic.behavior.core.Actor;
import org.terasology.logic.behavior.core.BaseAction;
import org.terasology.logic.behavior.core.BehaviorState;
import org.terasology.metalrenegades.ai.component.NearbyCitizenEnemiesComponent;

/**
* Action which sets this agent's move target to the nearest citizen from an enemy faction, as
* defined in {@link NearbyCitizenEnemiesComponent}.
*/
@BehaviorAction(name = "set_target_to_enemy")
public class SetTargetToEnemyAction extends BaseAction {

@Override
public BehaviorState modify(Actor actor, BehaviorState result) {
if (!actor.hasComponent(NearbyCitizenEnemiesComponent.class)) {
return BehaviorState.SUCCESS;
}

NearbyCitizenEnemiesComponent enemiesComponent = actor.getComponent(NearbyCitizenEnemiesComponent.class);
FollowComponent followComponent = new FollowComponent();

followComponent.entityToFollow = enemiesComponent.closestEnemy;
actor.getEntity().addComponent(followComponent);

return BehaviorState.SUCCESS;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2018 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.metalrenegades.ai.component;

import org.terasology.entitySystem.Component;
import org.terasology.metalrenegades.ai.system.FactionAlignmentSystem.Alignment;

/**
* Defines the faction alignment of a particular character, building, or settlement.
*/
public class FactionAlignmentComponent implements Component {

public Alignment alignment;

public FactionAlignmentComponent() {
this.alignment = Alignment.NEUTRAL;
}

public FactionAlignmentComponent(Alignment alignment) {
this.alignment = alignment;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2018 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.metalrenegades.ai.component;

import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityRef;

import java.util.List;

/**
* Stores the faction enemies in range of this citizen.
*/
public class NearbyCitizenEnemiesComponent implements Component {

public float searchRadius = 20f;

public List<EntityRef> enemiesWithinRange;

public EntityRef closestEnemy;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2018 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.metalrenegades.ai.event;

import org.terasology.entitySystem.event.Event;

/**
* Fired when a new citizen character is spawned by {@link org.terasology.metalrenegades.ai.system.CitizenSpawnSystem}.
*/
public class CitizenSpawnedEvent implements Event {

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.terasology.dialogs.components.DialogComponent;
import org.terasology.dialogs.components.DialogPage;
import org.terasology.dialogs.components.DialogResponse;
import org.terasology.dynamicCities.buildings.components.SettlementRefComponent;
import org.terasology.entitySystem.entity.EntityBuilder;
import org.terasology.entitySystem.entity.EntityManager;
import org.terasology.entitySystem.entity.EntityRef;
Expand All @@ -32,18 +33,17 @@
import org.terasology.logic.inventory.events.GiveItemEvent;
import org.terasology.logic.location.LocationComponent;
import org.terasology.metalrenegades.ai.CitizenNeed;
import org.terasology.metalrenegades.ai.component.CitizenComponent;
import org.terasology.metalrenegades.ai.component.FactionAlignmentComponent;
import org.terasology.metalrenegades.ai.component.HomeComponent;
import org.terasology.metalrenegades.ai.component.NeedsComponent;
import org.terasology.metalrenegades.ai.component.PotentialHomeComponent;
import org.terasology.metalrenegades.economy.MarketCitizenComponent;
import org.terasology.metalrenegades.ai.event.CitizenSpawnedEvent;
import org.terasology.metalrenegades.economy.TraderComponent;
import org.terasology.metalrenegades.economy.actions.ShowTradingScreenAction;
import org.terasology.registry.CoreRegistry;
import org.terasology.metalrenegades.minimap.events.AddCharacterToOverlayEvent;
import org.terasology.registry.In;

import java.util.ArrayList;
import java.util.Collection;

/**
* Spawns new citizens inside of available buildings with {@link PotentialHomeComponent}.
Expand All @@ -60,10 +60,19 @@ public class CitizenSpawnSystem extends BaseComponentSystem implements UpdateSub
private EntityManager entityManager;

@In
private PrefabManager prefabManager;
private InventoryManager inventoryManager;

@In
private InventoryManager inventoryManager;
private FactionAlignmentSystem citizenAlignmentSystem;

@In
private PrefabManager prefabManager;

@Override
public void initialise() {
// TODO: Temporary fix for injection malfunction in actions, ideally remove this in the future.
citizenAlignmentSystem = CoreRegistry.get(FactionAlignmentSystem.class);
}

@Override
public void update(float delta) {
Expand Down Expand Up @@ -98,12 +107,10 @@ public void update(float delta) {
* @return The new citizen entity, or null if spawning is not possible.
*/
private EntityRef spawnCitizen(EntityRef homeEntity) {
Prefab citizenPrefab = chooseCitizenPrefab();
if (citizenPrefab == null) { // if no prefab is available.
return null;
}
SettlementRefComponent settlementRefComponent = homeEntity.getComponent(SettlementRefComponent.class);
FactionAlignmentComponent settlementAlignmentComponent = settlementRefComponent.settlement.getComponent(FactionAlignmentComponent.class);

EntityBuilder entityBuilder = entityManager.newBuilder(chooseCitizenPrefab());
EntityBuilder entityBuilder = entityManager.newBuilder(citizenAlignmentSystem.getPrefab(settlementAlignmentComponent.alignment));

LocationComponent homeLocationComponent = homeEntity.getComponent(LocationComponent.class);
LocationComponent citizenLocationComponent = entityBuilder.getComponent(LocationComponent.class);
Expand All @@ -123,6 +130,7 @@ private EntityRef spawnCitizen(EntityRef homeEntity) {
needsComponent.needs.put(CitizenNeed.Type.REST, new CitizenNeed(50, 0.5f, 20, 50));

entityBuilder.saveComponent(needsComponent);
entityBuilder.addComponent(new TraderComponent());

EntityRef entityRef = entityBuilder.build();

Expand All @@ -131,25 +139,9 @@ private EntityRef spawnCitizen(EntityRef homeEntity) {
setupStartInventory(entityRef);
}

return entityRef;
}
entityRef.send(new CitizenSpawnedEvent());

/**
* Selects a random citizen prefab from a collection of prefabs with {@link CitizenComponent}.
*
* @return A random citizen prefab, or null if none are available.
*/
private Prefab chooseCitizenPrefab() {
Collection<Prefab> citizenList = prefabManager.listPrefabs(CitizenComponent.class);
citizenList.removeIf(prefab -> prefab.hasComponent(MarketCitizenComponent.class));

int i = (int) (Math.random() * citizenList.size());
for (Prefab prefab : citizenList) {
if (i-- <= 0) {
return prefab;
}
}
return null;
return entityRef;
}

private void setupStartInventory(EntityRef citizen) {
Expand Down

0 comments on commit 3b61ae0

Please sign in to comment.