Game::setInventory()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace App\Adventure;
4
5
use App\Adventure\Utils\Unpacker;
6
use App\Adventure\Utils\Formatter;
7
use App\Adventure\Utils\AvailableActionsProvider;
8
use App\Adventure\InputActions\InputActionInterface;
9
use App\Adventure\InputActions\ExamineAction;
10
use App\Adventure\InputActions\TakeAction;
11
use App\Adventure\InputActions\UseAction;
12
use App\Adventure\ActionResponses\LocationResponseInterface;
13
use Exception;
14
15
/**
16
 * Contains the core game logic, handling player actions, locations, and inventory.
17
 */
18
class Game
19
{
20
    /**
21
     * @var Location The current location of the player.
22
     */
23
    private $currentLocation;
24
25
    /**
26
     * @var Inventory The inventory of the player.
27
     */
28
    private $inventory;
29
30
    /**
31
     * Sets the current location of the player.
32
     *
33
     * @param Location $location The location object representing the current location.
34
     */
35 18
    public function setCurrentLocation(Location $location): void
36
    {
37 18
        $this->currentLocation = $location;
38
    }
39
40
    /**
41
     * Retrieves the current location of the player.
42
     *
43
     * @return Location The current location of the player, or null if not set.
44
     */
45 5
    public function getCurrentLocation(): Location
46
    {
47 5
        return $this->currentLocation;
48
    }
49
50
    /**
51
     * Sets the inventory of the player.
52
     *
53
     * @param Inventory $inventory The inventory object representing the player's inventory.
54
     */
55 13
    public function setInventory(Inventory $inventory): void
56
    {
57 13
        $this->inventory = $inventory;
58
    }
59
60
    /**
61
     * Retrieves the inventory of the player.
62
     *
63
     * @return Inventory The inventory object representing the player's inventory, or null if not set.
64
     */
65 1
    public function getInventory(): Inventory
66
    {
67 1
        return $this->inventory;
68
    }
69
70
    /**
71
     * Processes the player's action and target.
72
     *
73
     * @param  string $action The action to be performed.
74
     * @param  string $target The target of the action.
75
     * @return string The response message.
76
     */
77 31
    public function processAction(string $action, string $target): string
78
    {
79
        // Trim and convert the action and target to lowercase for consistent comparison
80 31
        $action = trim(strtolower($action));
81 31
        $target = trim(strtolower($target));
82
83 31
        if (!AvailableActionsProvider::isValidAction($action)) {
84 1
            return "That action is not recognized. Try using the 'help' action for assistance.";
85
        }
86
87
        switch ($action) {
88 30
            case 'where':
89 1
                return $this->handleWhereAction();
90 29
            case 'inventory':
91 2
                return $this->handleInventoryAction();
92 27
            case 'help':
93 9
                return $this->handleHelpAction($target);
94 18
            case 'go':
95 2
                return $this->handleGoAction($target);
96
            default:
97 16
                return $this->processItemAction($action, $target); // Additional processing for the item actions
98
        }
99
    }
100
101
    /**
102
     * Handles the 'where' action.
103
     *
104
     * @return string The response message containing the description of the current location.
105
     */
106 1
    private function handleWhereAction(): string
107
    {
108 1
        return Unpacker::unpackLocationDescriptions($this->currentLocation);
109
    }
110
111
    /**
112
     * Handles the 'inventory' action.
113
     *
114
     * @return string The response message containing the items in the inventory.
115
     */
116 2
    private function handleInventoryAction(): string
117
    {
118 2
        $itemStringsInInventory = $this->inventory->lookInInventory();
119
120 2
        $textResponse = "You look in your inventory:\n";
121
122 2
        if (empty($itemStringsInInventory)) {
123 1
            return $textResponse . "(empty)";
124
        }
125
126 1
        return $textResponse . implode("\n", $itemStringsInInventory);
127
    }
128
129
    /**
130
     * Handles the 'help' action.
131
     *
132
     * @param  string $target The target action to provide help for.
133
     * @return string The response message with the all available actions or information about the specified action.
134
     */
135 9
    private function handleHelpAction(string $target = ''): string
136
    {
137 9
        $availableActions = AvailableActionsProvider::getAvailableActions();
138
139 9
        if (empty($target)) {
140
            /** @var string */
141 1
            $formattedRows = Formatter::formatRows($availableActions, 4);
142
143 1
            return $formattedRows;
144
        }
145
146 8
        foreach ($availableActions as $action) {
147 8
            if ($target === $action['name']) {
148
                /** @var string */
149 7
                $formattedRow = Formatter::formatSingleRow($action, 4);
150
151 7
                return $formattedRow;
152
            }
153
        }
154
155 1
        return "The {$target} action is not recognized. Try using the 'help' action to see all available actions.";
156
    }
157
158
    /**
159
     * Handles the 'go' action.
160
     *
161
     * @param  string $direction The direction to go.
162
     * @return string The response message with the new location description or an error message.
163
     */
164 2
    private function handleGoAction(string $direction): string
165
    {
166 2
        $currentLocation = $this->currentLocation;
167
168 2
        if ($currentLocation->hasConnection($direction)) {
169
            /** @var Location */
170 1
            $newLocation = $currentLocation->getConnectedLocation($direction);
171
172 1
            $this->currentLocation = $newLocation;
173
174 1
            return Unpacker::unpackLocationDescriptions($newLocation);
0 ignored issues
show
Bug introduced by
It seems like $newLocation can also be of type null; however, parameter $location of App\Adventure\Utils\Unpa...kLocationDescriptions() does only seem to accept App\Adventure\Location, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
            return Unpacker::unpackLocationDescriptions(/** @scrutinizer ignore-type */ $newLocation);
Loading history...
175
        }
176
177 1
        return "You cannot go that way.";
178
    }
179
180
    /**
181
     * Processes the item action.
182
     *
183
     * @param  string $action The action to perform on the item.
184
     * @param  string $target The target item to perform the action on.
185
     * @return string The response message based on the action and target.
186
     */
187 16
    private function processItemAction(string $action, string $target): string
188
    {
189 16
        if (empty($target)) {
190 3
            return "You must specify a target to {$action}.";
191
        }
192
193 13
        $itemObject = $this->getItemToHandleAction($target);
194
195 13
        if ($itemObject === null) {
196 3
            return "There is no {$target} to {$action}.";
197
        }
198
199 10
        if (!$itemObject->hasAction($action)) {
200 3
            return "You cannot {$action} the {$target}.";
201
        }
202
203
        /** @var InputActionInterface */
204 7
        $actionObject = $itemObject->getAction($action);
205
206
        switch ($action) {
207 7
            case 'examine':
208 3
                return $this->handleExamineAction($itemObject, $actionObject);
0 ignored issues
show
Bug introduced by
It seems like $actionObject can also be of type null; however, parameter $actionObject of App\Adventure\Game::handleExamineAction() does only seem to accept App\Adventure\InputActions\InputActionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

208
                return $this->handleExamineAction($itemObject, /** @scrutinizer ignore-type */ $actionObject);
Loading history...
209 4
            case 'take':
210 2
                return $this->handleTakeAction($itemObject, $actionObject);
0 ignored issues
show
Bug introduced by
It seems like $actionObject can also be of type null; however, parameter $actionObject of App\Adventure\Game::handleTakeAction() does only seem to accept App\Adventure\InputActions\InputActionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

210
                return $this->handleTakeAction($itemObject, /** @scrutinizer ignore-type */ $actionObject);
Loading history...
211 2
            case 'use':
212 2
                return $this->handleUseAction($itemObject, $actionObject);
0 ignored issues
show
Bug introduced by
It seems like $actionObject can also be of type null; however, parameter $actionObject of App\Adventure\Game::handleUseAction() does only seem to accept App\Adventure\InputActions\InputActionInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

212
                return $this->handleUseAction($itemObject, /** @scrutinizer ignore-type */ $actionObject);
Loading history...
213
            default:
214
                return "The {$target} action is not recognized. Try using the 'help' action for assistance.";
215
        }
216
    }
217
218
    /**
219
     * Handles the 'examine' action for an item.
220
     *
221
     * @param Item                 $itemObject   The item to examine.
222
     * @param InputActionInterface $actionObject The action object associated with the action.
223
     * @return string The response message from the examine action.
224
     */
225 3
    private function handleExamineAction(Item $itemObject, InputActionInterface $actionObject): string
226
    {
227 3
        $textResponse = $actionObject->getTextResponse();
228
229 3
        if (!$textResponse) {
230 1
            $itemName = $itemObject->getName();
231
232 1
            return "Upon examining the {$itemName}, you find nothing noteworthy. It appears to be just an ordinary {$itemName}.";
233
        }
234
235 2
        return $textResponse;
236
    }
237
238
    /**
239
     * Handles the 'take' action for an item.
240
     *
241
     * @param Item                 $itemObject   The item to take.
242
     * @param InputActionInterface $actionObject The action object associated with the action.
243
     * @return string The response message from the take action or an error message.
244
     */
245 2
    private function handleTakeAction(Item $itemObject, InputActionInterface $actionObject): string
246
    {
247 2
        $itemName = $itemObject->getName();
248
249
        // Make sure the item has not been added to the inventory already before taking it
250 2
        if ($this->inventory->hasItem($itemName)) {
251 1
            return "The {$itemName} has already been added to your inventory.";
252
        }
253
254 1
        $this->inventory->addItem($itemObject);
255 1
        $this->currentLocation->removeItem($itemObject);
256
257 1
        $textResponse = $actionObject->getTextResponse();
258
259 1
        if (!$textResponse) {
260
            return "The {$itemName} has been added to your inventory.";
261
        }
262
263 1
        return $textResponse;
264
    }
265
266
    /**
267
     * Handles the 'use' action for an item.
268
     *
269
     * @param Item                 $itemObject   The item to use.
270
     * @param InputActionInterface $actionObject The action object associated with the action.
271
     * @return string The response message from the use action or an error message.
272
     */
273 2
    private function handleUseAction(Item $itemObject, InputActionInterface $actionObject): string
274
    {
275 2
        $itemName = $itemObject->getName();
276
277
        // Make sure the item is in the inventory before attempting to use it
278 2
        if (!$this->inventory->hasItem($itemName)) {
279 1
            return "Make sure you have the {$itemName} before attempting to use it.";
280
        }
281
282 1
        if (!$this->isCurrentLocationValidForAction($actionObject)) {
283
            return "You hold the {$itemName} in your hands, eager to use it. But alas, this is not the place.";
284
        }
285
286 1
        $newLocationDescription = $this->performLocationResponse($actionObject);
287
288 1
        $this->inventory->removeItem($itemObject);
289
290 1
        $textResponse = $actionObject->getTextResponse();
291
292 1
        return $textResponse . "\n\n" . $newLocationDescription;
293
    }
294
295
    /**
296
     * Checks if the current location is valid for the given action.
297
     *
298
     * @param InputActionInterface $actionObject The action object associated with the action.
299
     * @return bool Whether the current location is valid for the action.
300
     */
301 1
    private function isCurrentLocationValidForAction(InputActionInterface $actionObject): bool
302
    {
303 1
        $requiredLocation = $actionObject->getRequiredLocation();
304
305 1
        if ($requiredLocation === null) {
306 1
            return true; // No location requirement means all locations are valid
307
        }
308
309
        return $requiredLocation === $this->currentLocation;
310
    }
311
312
    /**
313
     * Tries to find the item in the current location or inventory and return it.
314
     *
315
     * @param string $target The target item.
316
     * @return Item|null The item object if found, null otherwise.
317
     */
318 13
    private function getItemToHandleAction(string $target): ?Item
319
    {
320 13
        if ($this->currentLocation->hasItem($target)) {
321 8
            return $this->currentLocation->getItem($target);
322
        }
323
324 5
        if ($this->inventory->hasItem($target)) {
325 2
            return $this->inventory->getItem($target);
326
        }
327
328 3
        return null;
329
    }
330
331
    /**
332
     * Perform the location response action associated with the given action object.
333
     *
334
     * @param InputActionInterface $actionObject The action object containing the location response.
335
     * @return string The descriptions of connections and items in the resulting location.
336
     */
337 1
    private function performLocationResponse(InputActionInterface $actionObject): string
338
    {
339
        /** @var LocationResponseInterface */
340 1
        $locationResponseObject = $actionObject->getLocationResponse();
341
342
        // Execute the location response action and update the current location
343 1
        $resultLocation = $locationResponseObject->doLocationResponse($this->currentLocation);
344 1
        $this->currentLocation = $resultLocation;
345
346 1
        $textResponse = "A change has occurred. Behold, the updated location awaits:\n" . Unpacker::unpackLocationDescriptions($resultLocation);
347
348 1
        return $textResponse;
349
    }
350
}
351