Skip to content

Commit

Permalink
Extract character oracle to its own module
Browse files Browse the repository at this point in the history
  • Loading branch information
kevingranade committed Apr 23, 2020
1 parent d683c5f commit 31dcaec
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 137 deletions.
119 changes: 5 additions & 114 deletions src/behavior_oracle.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
#include "behavior_oracle.h"

#include <array>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <unordered_map>

#include "behavior.h"
#include "bodypart.h"
#include "character.h"
#include "inventory.h"
#include "item.h"
#include "itype.h"
#include "player.h"
#include "ret_val.h"
#include "value_ptr.h"
#include "weather.h"

static const std::string flag_FIRESTARTER( "FIRESTARTER" );
#include "character_oracle.h"
#include "monster_oracle.h"

namespace behavior
{
Expand All @@ -26,106 +16,6 @@ status_t return_running( const oracle_t * )
return running;
}

// To avoid a local minima when the character has access to warmth in a shelter but gets cold
// when they go outside, this method needs to only alert when travel time to known shelter
// approaches time to freeze.
status_t character_oracle_t::needs_warmth_badly() const
{
const player *p = dynamic_cast<const player *>( subject );
// Use player::temp_conv to predict whether the Character is "in trouble".
for( const body_part bp : all_body_parts ) {
if( p->temp_conv[ bp ] <= BODYTEMP_VERY_COLD ) {
return running;
}
}
return success;
}

status_t character_oracle_t::needs_water_badly() const
{
// Check thirst threshold.
if( subject->get_thirst() > 520 ) {
return running;
}
return success;
}

status_t character_oracle_t::needs_food_badly() const
{
// Check hunger threshold.
if( subject->get_hunger() >= 300 && subject->get_starvation() > 2500 ) {
return running;
}
return success;
}

status_t character_oracle_t::can_wear_warmer_clothes() const
{
const player *p = dynamic_cast<const player *>( subject );
// Check inventory for wearable warmer clothes, greedily.
// Don't consider swapping clothes yet, just evaluate adding clothes.
for( const auto &i : subject->inv.const_slice() ) {
const item &candidate = i->front();
if( candidate.get_warmth() > 0 || p->can_wear( candidate ).success() ) {
return running;
}
}
return failure;
}

status_t character_oracle_t::can_make_fire() const
{
// Check inventory for firemaking tools and fuel
bool tool = false;
bool fuel = false;
for( const auto &i : subject->inv.const_slice() ) {
const item &candidate = i->front();
if( candidate.has_flag( flag_FIRESTARTER ) ) {
tool = true;
if( fuel ) {
return running;
}
} else if( candidate.flammable() ) {
fuel = true;
if( tool ) {
return running;
}
}
}
return success;
}

status_t character_oracle_t::can_take_shelter() const
{
// See if we know about some shelter
// Don't know how yet.
return failure;
}

status_t character_oracle_t::has_water() const
{
// Check if we know about water somewhere
bool found_water = subject->inv.has_item_with( []( const item & cand ) {
return cand.is_food() && cand.get_comestible()->quench > 0;
} );
return found_water ? running : failure;
}

status_t character_oracle_t::has_food() const
{
// Check if we know about food somewhere
bool found_food = subject->inv.has_item_with( []( const item & cand ) {
return cand.is_food() && cand.get_comestible()->has_calories();
} );
return found_food ? running : failure;
}

// predicate_map doesn't have to live here, but for the time being it's pretty pointless
// to break it out into it's own module.
// In principle this can be populated with any function that has a matching signature.
// In practice each element is a pointer-to-function to one of the above methods so that
// They can have provlidged access to the subject's internals.

// Just a little helper to make populating predicate_map slightly less gross.
static std::function < status_t( const oracle_t * ) >
make_function( status_t ( character_oracle_t::* fun )() const )
Expand All @@ -144,4 +34,5 @@ std::unordered_map<std::string, std::function<status_t( const oracle_t * )>> pre
{ "npc_has_food", make_function( &character_oracle_t::has_food ) }
}
};

} // namespace behavior
23 changes: 0 additions & 23 deletions src/behavior_oracle.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include <string>
#include <unordered_map>

class Character;

namespace behavior
{
enum status_t : char;
Expand All @@ -25,27 +23,6 @@ class oracle_t

status_t return_running( const oracle_t * );

class character_oracle_t : public oracle_t
{
public:
character_oracle_t( const Character *subject ) {
this->subject = subject;
}
/**
* Predicates used by AI to determine goals.
*/
status_t needs_warmth_badly() const;
status_t needs_water_badly() const;
status_t needs_food_badly() const;
status_t can_wear_warmer_clothes() const;
status_t can_make_fire() const;
status_t can_take_shelter() const;
status_t has_water() const;
status_t has_food() const;
private:
const Character *subject;
};

extern std::unordered_map<std::string, std::function<status_t( const oracle_t * )>> predicate_map;

} // namespace behavior
Expand Down
116 changes: 116 additions & 0 deletions src/character_oracle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <array>
#include <list>
#include <memory>

#include "behavior.h"
#include "character_oracle.h"
#include "bodypart.h"
#include "character.h"
#include "inventory.h"
#include "item.h"
#include "itype.h"
#include "player.h"
#include "ret_val.h"
#include "value_ptr.h"
#include "weather.h"

static const std::string flag_FIRESTARTER( "FIRESTARTER" );

namespace behavior
{

// To avoid a local minima when the character has access to warmth in a shelter but gets cold
// when they go outside, this method needs to only alert when travel time to known shelter
// approaches time to freeze.
status_t character_oracle_t::needs_warmth_badly() const
{
const player *p = dynamic_cast<const player *>( subject );
// Use player::temp_conv to predict whether the Character is "in trouble".
for( const body_part bp : all_body_parts ) {
if( p->temp_conv[ bp ] <= BODYTEMP_VERY_COLD ) {
return running;
}
}
return success;
}

status_t character_oracle_t::needs_water_badly() const
{
// Check thirst threshold.
if( subject->get_thirst() > 520 ) {
return running;
}
return success;
}

status_t character_oracle_t::needs_food_badly() const
{
// Check hunger threshold.
if( subject->get_hunger() >= 300 && subject->get_starvation() > 2500 ) {
return running;
}
return success;
}

status_t character_oracle_t::can_wear_warmer_clothes() const
{
const player *p = dynamic_cast<const player *>( subject );
// Check inventory for wearable warmer clothes, greedily.
// Don't consider swapping clothes yet, just evaluate adding clothes.
for( const auto &i : subject->inv.const_slice() ) {
const item &candidate = i->front();
if( candidate.get_warmth() > 0 || p->can_wear( candidate ).success() ) {
return running;
}
}
return failure;
}

status_t character_oracle_t::can_make_fire() const
{
// Check inventory for firemaking tools and fuel
bool tool = false;
bool fuel = false;
for( const auto &i : subject->inv.const_slice() ) {
const item &candidate = i->front();
if( candidate.has_flag( flag_FIRESTARTER ) ) {
tool = true;
if( fuel ) {
return running;
}
} else if( candidate.flammable() ) {
fuel = true;
if( tool ) {
return running;
}
}
}
return success;
}

status_t character_oracle_t::can_take_shelter() const
{
// See if we know about some shelter
// Don't know how yet.
return failure;
}

status_t character_oracle_t::has_water() const
{
// Check if we know about water somewhere
bool found_water = subject->inv.has_item_with( []( const item & cand ) {
return cand.is_food() && cand.get_comestible()->quench > 0;
} );
return found_water ? running : failure;
}

status_t character_oracle_t::has_food() const
{
// Check if we know about food somewhere
bool found_food = subject->inv.has_item_with( []( const item & cand ) {
return cand.is_food() && cand.get_comestible()->has_calories();
} );
return found_food ? running : failure;
}

} // namespace behavior
34 changes: 34 additions & 0 deletions src/character_oracle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
#ifndef CATA_SRC_CHARACTER_ORACLE_H
#define CATA_SRC_CHARACTER_ORACLE_H

#include "behavior_oracle.h"

class Character;

namespace behavior
{

class character_oracle_t : public oracle_t
{
public:
character_oracle_t( const Character *subject ) {
this->subject = subject;
}
/**
* Predicates used by AI to determine goals.
*/
status_t needs_warmth_badly() const;
status_t needs_water_badly() const;
status_t needs_food_badly() const;
status_t can_wear_warmer_clothes() const;
status_t can_make_fire() const;
status_t can_take_shelter() const;
status_t has_water() const;
status_t has_food() const;
private:
const Character *subject;
};

} //namespace behavior
#endif // CATA_SRC_CHARACTER_ORACLE_H

0 comments on commit 31dcaec

Please sign in to comment.