1 /*
2  *  Copyright (C) 2011-2016  OpenDungeons Team
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "creatureaction/CreatureActionSearchJob.h"
19 
20 #include "creatureaction/CreatureActionGetFee.h"
21 #include "creatureaction/CreatureActionSearchFood.h"
22 #include "creatureaction/CreatureActionSleep.h"
23 #include "creatureaction/CreatureActionUseRoom.h"
24 #include "creatureaction/CreatureActionWalkToTile.h"
25 #include "creaturemood/CreatureMood.h"
26 #include "entities/Creature.h"
27 #include "entities/CreatureDefinition.h"
28 #include "entities/Tile.h"
29 #include "game/Seat.h"
30 #include "gamemap/GameMap.h"
31 #include "rooms/Room.h"
32 #include "rooms/RoomType.h"
33 #include "utils/Helper.h"
34 #include "utils/LogManager.h"
35 #include "utils/MakeUnique.h"
36 #include "utils/Random.h"
37 
action()38 std::function<bool()> CreatureActionSearchJob::action()
39 {
40     return std::bind(&CreatureActionSearchJob::handleSearchJob,
41         std::ref(mCreature), mForced);
42 }
43 
handleSearchJob(Creature & creature,bool forced)44 bool CreatureActionSearchJob::handleSearchJob(Creature& creature, bool forced)
45 {
46     // Current creature tile position
47     Tile* myTile = creature.getPositionTile();
48     if (myTile == nullptr)
49     {
50         creature.popAction();
51         return false;
52     }
53 
54     // If we are unhappy, we do not want to work
55     switch(creature.getMoodValue())
56     {
57         case CreatureMoodLevel::Upset:
58         {
59             // 20% chances of not working
60             if(Random::Int(0, 100) < 20)
61             {
62                 creature.popAction();
63                 return true;
64             }
65             break;
66         }
67         case CreatureMoodLevel::Angry:
68         case CreatureMoodLevel::Furious:
69         {
70             // We don't work
71             creature.popAction();
72             return true;
73         }
74         default:
75             // We can work
76             break;
77     }
78 
79     if(!creature.hasSlapEffect())
80     {
81         // The creature should look for gold after payday if the keeper is not broke
82         if((creature.getGoldFee() > 0) &&
83            (!creature.hasActionBeenTried(CreatureActionType::getFee)) &&
84            (creature.getSeat()->getGold() > 0))
85         {
86             creature.pushAction(Utils::make_unique<CreatureActionGetFee>(creature));
87             return true;
88         }
89 
90         // If we are sleepy, we go to bed unless we have been slapped
91         if (Random::Double(20.0, 30.0) > creature.getWakefulness())
92         {
93             creature.popAction();
94             creature.pushAction(Utils::make_unique<CreatureActionSleep>(creature));
95             return true;
96         }
97 
98         // If we are hungry, we try to find food unless we have been slapped
99         if (Random::Double(70.0, 80.0) < creature.getHunger())
100         {
101             creature.popAction();
102             creature.pushAction(Utils::make_unique<CreatureActionSearchFood>(creature, false));
103             return true;
104         }
105     }
106 
107     if(forced)
108     {
109         // We check if we can work in the given room
110         Room* room = myTile->getCoveringRoom();
111         for(const CreatureRoomAffinity& affinity : creature.getDefinition()->getRoomAffinity())
112         {
113             if(room == nullptr)
114                 continue;
115             if(room->getType() != affinity.getRoomType())
116                 continue;
117             if(affinity.getEfficiency() <= 0)
118                 continue;
119             if(!creature.getSeat()->canOwnedCreatureUseRoomFrom(room->getSeat()))
120                 continue;
121 
122             // It is the room responsibility to test if the creature is suited for working in it
123             if(room->hasOpenCreatureSpot(&creature))
124             {
125                 creature.pushAction(Utils::make_unique<CreatureActionUseRoom>(creature, *room, forced));
126                 return false;
127             }
128             break;
129         }
130 
131         // If we couldn't work on the room we were forced to, we stop trying
132         creature.popAction();
133         return true;
134     }
135 
136     // We get the room we like the most. If we are on such a room, we start working if we can
137     for(const CreatureRoomAffinity& affinity : creature.getDefinition()->getRoomAffinity())
138     {
139         // If likeness = 0, we don't consider working here
140         if(affinity.getLikeness() <= 0)
141             continue;
142 
143         // See if we are in the room we like the most. If yes and we can work, we stay. If no,
144         // We check if there is such a room somewhere else where we can go
145         if((myTile->getCoveringRoom() != nullptr) &&
146            (myTile->getCoveringRoom()->getType() == affinity.getRoomType()) &&
147            (creature.getSeat()->canOwnedCreatureUseRoomFrom(myTile->getCoveringRoom()->getSeat())))
148         {
149             Room* room = myTile->getCoveringRoom();
150             // If the efficiency is 0 or the room is a hatchery, we only wander in the room
151             if((affinity.getEfficiency() <= 0) ||
152                (room->getType() == RoomType::hatchery))
153             {
154                 int index = Random::Int(0, room->numCoveredTiles() - 1);
155                 Tile* tileDest = room->getCoveredTile(index);
156                 creature.setDestination(tileDest);
157                 return false;
158             }
159 
160             // It is the room responsibility to test if the creature is suited for working in it
161             if(room->hasOpenCreatureSpot(&creature))
162             {
163                 creature.pushAction(Utils::make_unique<CreatureActionUseRoom>(creature, *room, forced));
164                 return true;
165             }
166         }
167 
168         // We are not in a room of the good type or we couldn't use it. We check if there is a reachable room
169         // of the good type
170         std::vector<Tile*> rooms;
171         for(Room* room : creature.getGameMap()->getRooms())
172         {
173             if(room->getSeat() != creature.getSeat())
174                 continue;
175 
176             if(room->numCoveredTiles() <= 0)
177                 continue;
178 
179             if(room->getType() != affinity.getRoomType())
180                 continue;
181 
182             // If efficiency is 0, we just want to wander so no need to check if the room is available
183             if((affinity.getEfficiency() > 0) && !room->hasOpenCreatureSpot(&creature))
184                 continue;
185 
186             Tile* tile = room->getCoveredTile(0);
187             if(!creature.getGameMap()->pathExists(&creature, myTile, tile))
188                 continue;
189 
190             rooms.push_back(tile);
191         }
192 
193         if(rooms.empty())
194             continue;
195 
196         Tile* chosenTile = nullptr;
197         std::list<Tile*> tilePath = creature.getGameMap()->findBestPath(&creature, myTile, rooms, chosenTile);
198 
199         if(tilePath.empty() || (chosenTile == nullptr))
200             continue;
201 
202         std::vector<Ogre::Vector3> vectorPath;
203         creature.tileToVector3(tilePath, vectorPath, true, 0.0);
204         creature.setWalkPath(EntityAnimation::walk_anim, EntityAnimation::idle_anim, true, true, vectorPath);
205         creature.pushAction(Utils::make_unique<CreatureActionWalkToTile>(creature));
206         return false;
207     }
208 
209     // Default action
210     creature.popAction();
211     return true;
212 }
213