1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
class Task
{
protected:
Combat_Character* owner; // the owner of the task (ie, who is to perform it)
public:
Task(Combat_Character* c) // change the ctor to take it
{
owner = c;
}
// give yourself a virtual dtor so you can safely delete Task* pointers
// without downcasting
virtual ~Task() { }
// make a pure virtual Perform() action to have the appropriate action performed
// -- to respond to your 'Think' function below -- I have this function return a bool.
// it'll return 'true' if the task was completed / needs to be removed from the
// schedule and 'false' if the task is not complete yet and is still pending.
//
// Note that this is very simplistic, and a better approach might be to return an enum
// which signals different responses to a task outcome.
//
// An even better approach might be to return a Task or another kind of Response class
// that gives you more flexibility to determine another action to take.
//
//
//
// The bool is simple for this example, though. So while it might not be the best approach,
// it's what I'll use here.
virtual bool Perform() = 0;
};
class moving_to_destination_Task : public Task
{
protected:
Position destination;
public:
//put the new member in the ctor, and pass it to Task's ctor
moving_to_destination_Task(Combat_Character* c,int x, int y):Task(c)
{
destination.x = x;
destination.y = y;
}
// get rid of this. It's useless unless you have a moving_to_destination_Task pointer
// which you won't -- you'll only have abstract Task pointers
/* Position get_destination() const
{
return destination;
}*/
// instead, just move the character from your perform function:
virtual bool Perform()
{
// it might make more sense to have Combat_Character move itself, so you
// can pass the buck here:
return owner->Perform_Move(destination);
}
};
class attacking_Task : public Task
{
protected:
Combat_Character* target; // the character he's attacking
public:
// take the owner param here, too
attacking_Task(Combat_Character* owner_para,Combat_Character* target_para) : Task(owner_para)
{
if(target_para == NULL)
{
throw "Error!";
}
target = target_para;
}
/* // get rid of this
Combat_Character* get_target() const
{
return target;
}*/
// Perform the action. Like the above, it might make more sense to have
// Combat_Character do the actual attacking
virtual bool Perform()
{
return owner->Perform_Attack(target);
}
};
class Combat_Character
{
private:
//a bunch of other member variables here, including position, velocity and attack range
list<Task*> schedule;
public:
void receive_input()
{
if ( /*the user clicks on an enemy*/ )
{
if ( /*enemy is not within the attack range*/ )
{
//create a moving_to_destination_Task
schedule.push_back( new moving_to_destination_Task(x, y) );
}
//create an attacking_Task
schedule.push_back( new attacking_Task( /*pointer to enemy*/) );
}
}
/* I agree with helios that the 'think' function seems unnecessary.
so I deleted it here */
// Perform the action
void perform_action()
{
// perform the task from the schedule
if( schedule[0]->Perform() ) // that's all you have to do! No casting!
{
// if the task was completed.. remove it from the schedule
delete schedule[0]; // works fine because we have a virtual dtor. No need to cast
schedule.pop_front();
}
}
// Perform_Move
bool Perform_Move(const Position& pos)
{
if( /* already at destination */ )
{
// kill velocity
return true; // done with the move, this task is complete
}
else
{
// set velocity
return false; // not done with the move yet, keep task in schedule
}
}
// Perform_Attack
bool Perform_Attack(Combat_Character* target)
{
if( /* target is within range */ )
{
// attack the target
return true; // task complete
}
else
{
// otherwise, we're not in range
// so move towards the target first
schedule.push_back( new moving_to_destination_Task( this, x, y ) ); // ** NOTE **
// then attack the target again
schedule.push_back( new attack_Task( this,target ) );
return true; // complete (sort of -- because we added a new attack task)
/*
*** NOTE ***
since perform_action removes the front element of the schedule, you don't want to
push_front any tasks here. That's why I push the new tasks to the end instead of pushing them to the front.
Also beware that whenever you add/remove tasks from the schedule you may invalidate any iterators you're using
(depending on the container you're using -- I'm guessing deque from your use of [0] and pop_front -- but you may
want to consider using list instead for this very reason)
*/
}
}
};
| |