Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic factions #609

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
145 changes: 144 additions & 1 deletion src/ai/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@
#include "ai/squadlogic.h"

#include "worldobject/ship.h"
#include "equipment/weapons/bullet.h"
#include "equipment/weapons/rocket.h"

#include "player.h"

#include "factions/faction.h"
#include "factions/factionrelation.h"
#include "factions/factionmatrix.h"

#include "ui/hud/hud.h"

#include "worldobject/worldobjectinfo.h"

#include "world/world.h"


Character::Character(Ship& ship, Faction& faction):
m_ship(ship),
m_faction(&faction),
m_task(nullptr)
m_task(nullptr),
m_world(World::instance())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we have the Singleton, we should use it. Not some obscure pointer that was once retrieved from this singleton

{
}

Expand All @@ -38,4 +53,132 @@ void Character::update(float deltaSec) {
if (m_task.get()) {
m_task->update(deltaSec);
}
resetFriendliness(deltaSec);
}

void Character::onCollisionWith(WorldObject* worldObject) {
float relationModifier = 0;
std::string warningMessage;
WorldObject* aggressor;
switch (worldObject->objectType()) {
case WorldObjectType::Ship: {
Ship* ship = static_cast<Ship*>(worldObject);
aggressor = ship;
relationModifier = -0.5f;
warningMessage = m_ship.info().name() + ": Watch where you're going!";
}
break;
case WorldObjectType::Bullet: {
Bullet* bullet = static_cast<Bullet*>(worldObject);
aggressor = bullet->creator();
relationModifier = -1;
warningMessage = m_ship.info().name() + ": Check your fire!";
}
break;
case WorldObjectType::Rocket:{
Rocket* rocket = static_cast<Rocket*>(worldObject);
aggressor = rocket->creator();
relationModifier = -1;
warningMessage = m_ship.info().name() + ": Check your fire!";
}
break;
}
if (aggressor != m_world->player().ship()) {
return;
} else {
if (relationTypeTo(aggressor) != FactionRelationType::Enemy) {
m_world->player().hud().showMessage(warningMessage);
}
}
onAggressionBy(aggressor, relationModifier);
}

void Character::onAggressionBy(WorldObject* aggressor, float relationModifier) {
if (aggressor->objectType() != WorldObjectType::Ship) {
return;
}
Ship* ship = static_cast<Ship*>(aggressor);
float friendlinessToAggressorFaction = m_faction->relationTo(ship->character()->faction()).friendliness();
relationModifier *= 2.0f - glm::abs(friendlinessToAggressorFaction) / 100.0f;
changeFriendlinessToAggressor(ship, relationModifier);
}

void Character::onKilledBy(WorldObject* worldObject) {
if (worldObject->objectType() != WorldObjectType::Ship) {
return;
}
Ship* ship = static_cast<Ship*>(worldObject);
if (ship != m_world->player().ship()) {
return;
}
m_faction->changeFriendlinessToFaction(ship->character()->faction(), -10);
if (m_ship.squadLogic()->squad()) {
m_ship.squadLogic()->squad()->propagadeFriendlinessToWorldObject(ship, glm::min(-30.0f, m_faction->relationTo(ship->character()->faction()).friendliness()));
}
}

FactionRelationType Character::relationTypeTo(WorldObject* worldObject) {
if (m_friendlinessToWorldObject.count(makeHandle(worldObject)) > 0) {
return FactionRelation::type(m_friendlinessToWorldObject[makeHandle(worldObject)]);
}
if (worldObject->objectType() != WorldObjectType::Ship) {
return FactionRelationType::Neutral;
}
return m_faction->relationTo(static_cast<Ship*>(worldObject)->character()->faction()).type();
}

void Character::setFriendlinessToWorldObject(WorldObject* worldObject, float friendliness) {
m_friendlinessToWorldObject[makeHandle(worldObject)] = friendliness;
m_friendlinessResetDelay[makeHandle(worldObject)] = 10.0f;
}

void Character::changeFriendlinessToAggressor(WorldObject* aggressor, float difference) {
if (aggressor->objectType() != WorldObjectType::Ship) {
return;
}
Ship* ship = static_cast<Ship*>(aggressor);
float friendliness = m_friendlinessToWorldObject[makeHandle(aggressor)];
if (friendliness == 0.0f) {
friendliness = m_faction->relationTo(ship->character()->faction()).friendliness();
}
friendliness += difference;
if (FactionRelation::type(friendliness) == FactionRelationType::Enemy && m_ship.squadLogic()->squad().get()) {
m_ship.squadLogic()->squad()->propagadeFriendlinessToWorldObject(aggressor, friendliness);
}
m_world->factionMatrix().changeFriendlinessToFaction(*m_faction, ship->character()->faction(), difference / 10.0f);
setFriendlinessToWorldObject(aggressor, friendliness);
}

void Character::resetFriendliness(float deltaSec) {
for (auto it = m_friendlinessToWorldObject.begin(); it != m_friendlinessToWorldObject.end();) {
float friendliness = it->second;
if (!it->first.valid()) {
it = m_friendlinessToWorldObject.erase(it);
continue;
}
if (it->first->objectType() != WorldObjectType::Ship) {
it = m_friendlinessToWorldObject.erase(it);
continue;
}
const Ship* ship = static_cast<const Ship*>(it->first.get());
if (m_friendlinessResetDelay.count(it->first) > 0) {
if (m_friendlinessResetDelay[it->first] > 0) {
m_friendlinessResetDelay[it->first] -= deltaSec;
it++;
continue;
} else {
m_friendlinessResetDelay.erase(it->first);
}
}
if (m_faction->relationTo(ship->character()->faction()).friendliness() > friendliness) {
it->second += deltaSec;
} else {
it->second -= deltaSec;
}
if (glm::abs(m_faction->relationTo(ship->character()->faction()).friendliness() - it->second) < 0.1f) {
it = m_friendlinessToWorldObject.erase(it);
} else {
it++;
}
}
}
23 changes: 23 additions & 0 deletions src/ai/character.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#pragma once

#include <memory>
#include <unordered_map>

#include "factions/factionrelation.h"
#include "utils/handle/handle.h"
#include "utils/handle/handlehash.h"


class AiTask;
class Faction;
class Ship;
class WorldObject;
class World;

/**
* The Character is the Ship's pilot and executes his AiTask. He has a Faction which decides
Expand All @@ -23,9 +30,25 @@ class Character {

virtual void update(float deltaSec);

void onCollisionWith(WorldObject* worldObject);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the methods you added here should probably be protected


void onKilledBy(WorldObject* worldObject);

void onAggressionBy(WorldObject* aggressor, float friendlinessModifier);

FactionRelationType relationTypeTo(WorldObject* worldObject);

void setFriendlinessToWorldObject(WorldObject* worldObject, float friendliness);

protected:
World* m_world;
Ship& m_ship;
Faction* m_faction;
std::shared_ptr<AiTask> m_task;
std::unordered_map<Handle<WorldObject>, float> m_friendlinessToWorldObject;
std::unordered_map<Handle<WorldObject>, float> m_friendlinessResetDelay;

void resetFriendliness(float deltaSec);
void changeFriendlinessToAggressor(WorldObject* aggressor, float difference);
};

7 changes: 4 additions & 3 deletions src/ai/grouptasks/defendareatask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <memory>

#include "worldobject/ship.h"
#include "player.h"
#include "ai/squadlogic.h"
#include "ai/squad.h"
#include "ai/character.h"
Expand Down Expand Up @@ -75,10 +76,10 @@ bool DefendAreaTask::isEnemyInRange() {
for (WorldObject *worldObject : query.intersectingWorldObjects()) {
Ship* ship = dynamic_cast<Ship*>(worldObject);
if (ship) {
Faction& enemyFaction = ship->character()->faction();
if (enemyFaction.relationTo(m_squad.leader()->character()->faction()).type() == FactionRelationType::Enemy) {
if (m_squad.leader()->character()->relationTypeTo(ship) == FactionRelationType::Enemy) {
m_enemies.push_back(makeHandle(worldObject));
} else {
}
else {
continue;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/ai/squad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "utils/safenormalize.h"
#include "physics/physics.h"
#include "voxel/voxelclusterbounds.h"
#include "ai/character.h"

Squad::Squad(Ship* leader) :
m_leader(leader),
Expand Down Expand Up @@ -125,3 +126,9 @@ glm::vec3 Squad::calculateFormationPosition(Ship* member, int position) {
return m_leader->transform().position() + m_leader->physics().speed().directional() + m_leader->transform().orientation() * (distance * glm::normalize(direction));
}

void Squad::propagadeFriendlinessToWorldObject(WorldObject* worldObject, float friendliness) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please name it something like propagateToMembers or something. The current name is wrong

m_leader->character()->setFriendlinessToWorldObject(worldObject, friendliness);
for (Ship* ship : m_members) {
ship->character()->setFriendlinessToWorldObject(worldObject, friendliness);
}
}
3 changes: 3 additions & 0 deletions src/ai/squad.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

class Ship;
class AiGroupTask;
class WorldObject;

/**
* Ships can be joined to Squads to give them AiGroupTasks. The Squad's leader is the one to execute the Squad's task.
Expand All @@ -25,6 +26,8 @@ class Squad : public Scriptable {

const std::vector<Ship*>& members();

//void propagadeFriendlinessToPlayer(float friendliness);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used? remove!

void propagadeFriendlinessToWorldObject(WorldObject* worldObject, float friendliness);

protected:
friend class SquadLogic;
Expand Down
4 changes: 4 additions & 0 deletions src/factions/faction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ const std::string& Faction::printName() const {
FactionRelation& Faction::relationTo(Faction& other) {
return World::instance()->factionMatrix().getRelation(*this, other);
}

void Faction::changeFriendlinessToFaction(Faction& other, float friendliness) {
World::instance()->factionMatrix().changeFriendlinessToFaction(*this, other, friendliness);
}
2 changes: 2 additions & 0 deletions src/factions/faction.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class Faction {

FactionRelation& relationTo(Faction& other);

void changeFriendlinessToFaction(Faction& other, float difference);

protected:
std::string m_key;
std::string m_printName;
Expand Down
14 changes: 14 additions & 0 deletions src/factions/factionmatrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ FactionRelation& FactionMatrix::getRelationToPlayer(Faction& faction) {
return getRelation(faction, playerFaction());
}

void FactionMatrix::changeFriendlinessToPlayer(Faction& faction, float difference) {
changeFriendlinessToFaction(faction, playerFaction(), difference);
}

void FactionMatrix::changeFriendlinessToFaction(Faction& faction, Faction& otherFaction, float difference) {
getRelation(faction, otherFaction).changeFriendliness(difference); //change friendliness to other faction
for (auto it = m_factions.begin(); it != m_factions.end(); ++it) { //change all other factions friendliness to other faction
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose(!) this loops propagtes the difference damped to the other functions. Please give this block a descriptive name by putting it in some other function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you use this iterator loop? I don't see any reason not to use a foreach loop?

if (it->second.get() != &otherFaction && it->second.get() != &faction) {
float friendliness = getRelation(faction, *it->second.get()).friendliness();
getRelation(*it->second.get(), otherFaction).changeFriendliness(difference*(friendliness / 100));
}
}
}

void FactionMatrix::setupRelations() {
getRelation(playerFaction(), pirateFaction()).setFriendliness(-20.0f);
getRelation(playerFaction(), policeFaction()).setFriendliness(10.0f);
Expand Down
3 changes: 3 additions & 0 deletions src/factions/factionmatrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class FactionMatrix {
FactionRelation& getRelation(Faction& factionA, Faction& factionB);
FactionRelation& getRelationToPlayer(Faction& faction);

void changeFriendlinessToPlayer(Faction& faction, float difference);
void changeFriendlinessToFaction(Faction& faction, Faction& otherFaction, float difference);

protected:
std::unordered_map<std::string, std::shared_ptr<Faction>> m_factions;
std::unordered_map<std::pair<Faction*, Faction*>, std::shared_ptr<FactionRelation>> m_relations;
Expand Down
21 changes: 15 additions & 6 deletions src/factions/factionrelation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cassert>

#include "faction.h"
#include "glm/glm.hpp"


FactionRelation::FactionRelation(Faction& factionA, Faction& factionB, float friendliness):
Expand All @@ -29,24 +30,32 @@ void FactionRelation::setFriendliness(float friendliness) {
}

bool FactionRelation::isHostile() const {
return m_friendliness <= -30.0f;
return type() == FactionRelationType::Enemy;
}

bool FactionRelation::isFriendly() const {
return m_friendliness >= 30.0f;
return type() == FactionRelationType::Friend;
}

FactionRelationType FactionRelation::type() const {
if (m_friendliness <= -30.0f) {
return type(m_friendliness);
}

void FactionRelation::changeFriendliness(float difference) {
m_friendliness = glm::max(-100.0f, glm::min(100.0f, m_friendliness + difference));
}

FactionRelationType FactionRelation::type(float friendliness) {
if (friendliness <= -30.0f) {
return FactionRelationType::Enemy;
}
if (m_friendliness <= -5.0f) {
if (friendliness <= -5.0f) {
return FactionRelationType::NegativeNeutral;
}
if (m_friendliness <= 5.0f) {
if (friendliness <= 5.0f) {
return FactionRelationType::Neutral;
}
if (m_friendliness <= 30.0f) {
if (friendliness <= 30.0f) {
return FactionRelationType::PositiveNeutral;
}
return FactionRelationType::Friend;
Expand Down
2 changes: 2 additions & 0 deletions src/factions/factionrelation.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ class FactionRelation {

float friendliness() const;
void setFriendliness(float friendliness);
void changeFriendliness(float difference);

FactionRelationType type() const;

bool isHostile() const;
bool isFriendly() const;

static std::string typeName(FactionRelationType type);
static FactionRelationType type(float friendliness);


protected:
Expand Down
4 changes: 4 additions & 0 deletions src/ui/hud/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ void HUD::showMessage(const std::string& message) {
m_elements->showMessage(message);
}

void HUD::showCommunicationMessage(const std::string& message) {
m_elements->showCommunicationMessage(message);
}

void HUD::updateScanner(float deltaSec) {
if (m_player->ship()) {
m_scanner->update(deltaSec, m_player->ship());
Expand Down
1 change: 1 addition & 0 deletions src/ui/hud/hud.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class HUD {
void showMissionInfo(const std::string& title, const std::string& caption);
void showMissionMessage(const std::string& message);
void showMessage(const std::string& message);
void showCommunicationMessage(const std::string& message);


protected:
Expand Down
Loading