Passed
Pull Request — master (#858)
by Roy
06:37
created
deploy/lib/control/ClanController.php 2 patches
Indentation   +62 added lines, -62 removed lines patch added patch discarded remove patch
@@ -23,20 +23,20 @@  discard block
 block discarded – undo
23 23
 	/**
24 24
 	 * View information about a clan
25 25
 	 *
26
-     * @param Container
26
+	 * @param Container
27 27
 	 * @return Response
28 28
 	 * @note
29 29
 	 * If a clan_id is not specified, the clan of the current user will be used
30 30
 	 */
31 31
 	public function view(Container $p_dependencies) {
32
-        $clanID = RequestWrapper::getPostOrGet('clan_id', null);
33
-        $player = $p_dependencies['current_player'];
32
+		$clanID = RequestWrapper::getPostOrGet('clan_id', null);
33
+		$player = $p_dependencies['current_player'];
34 34
 
35
-        if ($clanID === null && $player) {
36
-            $clan = Clan::findByMember($player);
37
-        } else {
38
-            $clan = $clanID? Clan::find($clanID) : null;
39
-        }
35
+		if ($clanID === null && $player) {
36
+			$clan = Clan::findByMember($player);
37
+		} else {
38
+			$clan = $clanID? Clan::find($clanID) : null;
39
+		}
40 40
 
41 41
 		if (isset($clan) && $clan instanceof Clan) {
42 42
 			$parts = [
@@ -85,7 +85,7 @@  discard block
 block discarded – undo
85 85
 	/**
86 86
 	 * Send an invitation to a character that will ask them to join your clan
87 87
 	 *
88
-     * @param Container
88
+	 * @param Container
89 89
 	 * @return Response
90 90
 	 * @throws Exception You cannot use this function if you are not the leader of a clan
91 91
 	 */
@@ -127,7 +127,7 @@  discard block
 block discarded – undo
127 127
 	/**
128 128
 	 * Removes the active player from the clan they are in
129 129
 	 *
130
-     * @param Container
130
+	 * @param Container
131 131
 	 * @return Response
132 132
 	 * @throws Exception If you are the only leader of your clan, you cannot leave, you must disband
133 133
 	 */
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 	/**
156 156
 	 * Creates a new clan with the current user as the leader
157 157
 	 *
158
-     * @param Container
158
+	 * @param Container
159 159
 	 * @return Response
160 160
 	 * @note
161 161
 	 * Player must be high enough level to create a clan
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
 	/**
200 200
 	 * Sends a request to a clan leader for the current user to join a clan
201 201
 	 *
202
-     * @param Container
202
+	 * @param Container
203 203
 	 * @return Response
204 204
 	 */
205 205
 	public function join(Container $p_dependencies) {
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
 	/**
226 226
 	 * Deletes a clan and messages all members that it has been disbanded
227 227
 	 *
228
-     * @param Container
228
+	 * @param Container
229 229
 	 * @return Response
230 230
 	 * @throws \Exception The player disbanding must be the leader of the clan
231 231
 	 */
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 	/**
266 266
 	 * Removes a player from a clan
267 267
 	 *
268
-     * @param Container
268
+	 * @param Container
269 269
 	 * @return Response
270 270
 	 * @throws Exception The player must be the leader of the clan to kick a member
271 271
 	 */
@@ -295,15 +295,15 @@  discard block
 block discarded – undo
295 295
 	/**
296 296
 	 * Edits clan metadata
297 297
 	 *
298
-     * @todo accumulate error messages
299
-     * @param Container
298
+	 * @todo accumulate error messages
299
+	 * @param Container
300 300
 	 * @return Response
301 301
 	 * @throws Exception Only leaders can update clan details
302 302
 	 * @note
303 303
 	 * All parameters are options
304 304
 	 */
305 305
 	public function update(Container $p_dependencies) {
306
-        $request = RequestWrapper::$request;
306
+		$request = RequestWrapper::$request;
307 307
 		$player  = $p_dependencies['current_player'];
308 308
 		$clan    = Clan::findByMember($player);
309 309
 
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
 	/**
366 366
 	 * Shows a form for editing clan metadata
367 367
 	 *
368
-     * @param Container
368
+	 * @param Container
369 369
 	 * @return Response
370 370
 	 * @see update()
371 371
 	 */
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 	/**
387 387
 	 * Sends a message to all members of the clan of the current player
388 388
 	 *
389
-     * @param Container
389
+	 * @param Container
390 390
 	 * @return Response
391 391
 	 */
392 392
 	public function message(Container $p_dependencies) {
@@ -429,7 +429,7 @@  discard block
 block discarded – undo
429 429
 	/**
430 430
 	 * Shows a ranked list of all clans in the game
431 431
 	 *
432
-     * @param Container
432
+	 * @param Container
433 433
 	 * @return Response
434 434
 	 */
435 435
 	public function listClans(Container $p_dependencies) {
@@ -457,11 +457,11 @@  discard block
 block discarded – undo
457 457
 	/**
458 458
 	 * Review a request to join a clan
459 459
 	 *
460
-     * @param Container
460
+	 * @param Container
461 461
 	 * @return Response
462 462
 	 */
463 463
 	public function review(Container $p_dependencies) {
464
-        $request = RequestWrapper::$request;
464
+		$request = RequestWrapper::$request;
465 465
 		$ninja   = $p_dependencies['current_player'];
466 466
 		$clan    = Clan::findByMember($ninja);
467 467
 
@@ -494,14 +494,14 @@  discard block
 block discarded – undo
494 494
 	/**
495 495
 	 * Accept a player as a new member of a clan
496 496
 	 *
497
-     * @param Container
497
+	 * @param Container
498 498
 	 * @return Response
499 499
 	 *
500 500
 	 * @par Preconditions:
501 501
 	 * Active player must be leader of a clan
502 502
 	 */
503 503
 	public function accept(Container $p_dependencies) {
504
-        $request = RequestWrapper::$request;
504
+		$request = RequestWrapper::$request;
505 505
 		$ninja   = $p_dependencies['current_player'];
506 506
 		$clan    = Clan::findByMember($ninja);
507 507
 
@@ -545,7 +545,7 @@  discard block
 block discarded – undo
545 545
 	/**
546 546
 	 * Generates a Response for rendering pages
547 547
 	 *
548
-     * @todo inject dependencies instead of pulling from the model
548
+	 * @todo inject dependencies instead of pulling from the model
549 549
 	 * @param Array $p_parts Name-Value pairs of values to send to the view
550 550
 	 * @return Response
551 551
 	 */
@@ -567,12 +567,12 @@  discard block
 block discarded – undo
567 567
 
568 568
 		$p_parts['clan_creator_min_level'] = self::CLAN_CREATOR_MIN_LEVEL;
569 569
 
570
-        $options  = [
571
-            'body_classes' => 'clan',
572
-            'quickstat'    => true,
573
-        ];
570
+		$options  = [
571
+			'body_classes' => 'clan',
572
+			'quickstat'    => true,
573
+		];
574 574
 
575
-        return new StreamedViewResponse($p_parts['title'], 'clan.tpl', $p_parts, $options);
575
+		return new StreamedViewResponse($p_parts['title'], 'clan.tpl', $p_parts, $options);
576 576
 	}
577 577
 
578 578
 	/**
@@ -594,39 +594,39 @@  discard block
 block discarded – undo
594 594
 		return false;
595 595
 	}
596 596
 
597
-    /**
598
-     * ????
599
-     *
600
-     * @todo Simplify this invite system.
601
-     * @param int $user_id
602
-     * @param int $clan_id
603
-     * @return void
604
-     */
605
-    private function sendClanJoinRequest($user_id, $clan_id) {
606
-        DatabaseConnection::getInstance();
607
-        $clan_obj  = new Clan($clan_id);
608
-        $leader    = $clan_obj->getLeaderInfo();
609
-        $leader_id = $leader['player_id'];
610
-        $user      = Player::find($user_id);
611
-        $username  = $user->name();
612
-
613
-        $confirmStatement = DatabaseConnection::$pdo->prepare('SELECT verification_number FROM players WHERE player_id = :user');
614
-        $confirmStatement->bindValue(':user', $user_id);
615
-        $confirmStatement->execute();
616
-        $confirm = $confirmStatement->fetchColumn();
617
-
618
-        // These ampersands get encoded later.
619
-        $url = "[href:clan/review/?joiner=$user_id&confirmation=$confirm|Confirm Request]";
620
-
621
-        $join_request_message = 'CLAN JOIN REQUEST: '.htmlentities($username)." has sent a request to join your clan.
597
+	/**
598
+	 * ????
599
+	 *
600
+	 * @todo Simplify this invite system.
601
+	 * @param int $user_id
602
+	 * @param int $clan_id
603
+	 * @return void
604
+	 */
605
+	private function sendClanJoinRequest($user_id, $clan_id) {
606
+		DatabaseConnection::getInstance();
607
+		$clan_obj  = new Clan($clan_id);
608
+		$leader    = $clan_obj->getLeaderInfo();
609
+		$leader_id = $leader['player_id'];
610
+		$user      = Player::find($user_id);
611
+		$username  = $user->name();
612
+
613
+		$confirmStatement = DatabaseConnection::$pdo->prepare('SELECT verification_number FROM players WHERE player_id = :user');
614
+		$confirmStatement->bindValue(':user', $user_id);
615
+		$confirmStatement->execute();
616
+		$confirm = $confirmStatement->fetchColumn();
617
+
618
+		// These ampersands get encoded later.
619
+		$url = "[href:clan/review/?joiner=$user_id&confirmation=$confirm|Confirm Request]";
620
+
621
+		$join_request_message = 'CLAN JOIN REQUEST: '.htmlentities($username)." has sent a request to join your clan.
622 622
             If you wish to allow this ninja into your clan click the following link:
623 623
                 $url";
624 624
 
625
-        Message::create([
626
-            'send_from' => $user_id,
627
-            'send_to'   => $leader_id,
628
-            'message'   => $join_request_message,
629
-            'type'      => 0,
630
-        ]);
631
-    }
625
+		Message::create([
626
+			'send_from' => $user_id,
627
+			'send_to'   => $leader_id,
628
+			'message'   => $join_request_message,
629
+			'type'      => 0,
630
+		]);
631
+	}
632 632
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -35,7 +35,7 @@  discard block
 block discarded – undo
35 35
         if ($clanID === null && $player) {
36 36
             $clan = Clan::findByMember($player);
37 37
         } else {
38
-            $clan = $clanID? Clan::find($clanID) : null;
38
+            $clan = $clanID ? Clan::find($clanID) : null;
39 39
         }
40 40
 
41 41
 		if (isset($clan) && $clan instanceof Clan) {
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
 			$default_clan_name = 'Clan '.$player->name();
170 170
 
171 171
 			while (!Clan::isUniqueClanName($default_clan_name)) {
172
-				$default_clan_name = $default_clan_name.rand(1,999);
172
+				$default_clan_name = $default_clan_name.rand(1, 999);
173 173
 			}
174 174
 
175 175
 			$clan = Clan::create($player, $default_clan_name);
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
 	 * @return Response
204 204
 	 */
205 205
 	public function join(Container $p_dependencies) {
206
-		$clanID = (int) RequestWrapper::getPostOrGet('clan_id', 0);
206
+		$clanID = (int)RequestWrapper::getPostOrGet('clan_id', 0);
207 207
 		$clan   = Clan::find($clanID);
208 208
 
209 209
 		$this->sendClanJoinRequest($p_dependencies['session']->get('player_id'), $clanID);
@@ -340,7 +340,7 @@  discard block
 block discarded – undo
340 340
 
341 341
 		// Truncate at 500 chars if necessary.
342 342
 		$truncated_clan_desc = substr((string)$new_clan_description, 0, 500);
343
-		if ($truncated_clan_desc != (string) $new_clan_description) {
343
+		if ($truncated_clan_desc != (string)$new_clan_description) {
344 344
 			$new_clan_description = $truncated_clan_desc;
345 345
 		}
346 346
 
@@ -466,7 +466,7 @@  discard block
 block discarded – undo
466 466
 		$clan    = Clan::findByMember($ninja);
467 467
 
468 468
 		$joiner = Player::find($request->get('joiner'));
469
-		$confirmation = (int) $request->get('confirmation');
469
+		$confirmation = (int)$request->get('confirmation');
470 470
 
471 471
 		$parts = [
472 472
 			'title' => 'Accept a New Clan Member',
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
 		$clan    = Clan::findByMember($ninja);
507 507
 
508 508
 		$joiner = Player::find($request->get('joiner'));
509
-		$confirmation = (int) $request->get('confirmation');
509
+		$confirmation = (int)$request->get('confirmation');
510 510
 
511 511
 		$parts = [
512 512
 			'title' => 'Accept a New Clan Member',
@@ -567,7 +567,7 @@  discard block
 block discarded – undo
567 567
 
568 568
 		$p_parts['clan_creator_min_level'] = self::CLAN_CREATOR_MIN_LEVEL;
569 569
 
570
-        $options  = [
570
+        $options = [
571 571
             'body_classes' => 'clan',
572 572
             'quickstat'    => true,
573 573
         ];
Please login to merge, or discard this patch.
deploy/lib/control/NpcController.php 2 patches
Indentation   +614 added lines, -614 removed lines patch added patch discarded remove patch
@@ -19,618 +19,618 @@
 block discarded – undo
19 19
  * Handles displaying npcs and attacking specific npcs
20 20
  */
21 21
 class NpcController extends AbstractController {
22
-    const ALIVE                      = true;
23
-    const PRIV                       = false;
24
-    const HIGH_TURNS                 = 50;
25
-    const ITEM_DECREASES_GOLD_DIVISOR = 1.11;
26
-    const ONI_DAMAGE_CAP             = 20;
27
-    const RANDOM_ENCOUNTER_DIVISOR   = 400;
28
-    const SAMURAI_REWARD_DMG         = 100;
29
-    const ONI_TURN_LOSS              = 10;
30
-    const ONI_KILL_LOSS              = 1;
31
-    const MIN_LEVEL_FOR_BOUNTY       = 5;
32
-    const MAX_LEVEL_FOR_BOUNTY       = 50;
33
-
34
-    public static $STEALTH_REMOVING_NPCS = ['samurai', 'oni'];
35
-
36
-    private $randomness = null;
37
-
38
-    /**
39
-     * Inject different seed when non-randomness is needed (for testing)
40
-     */
41
-    public function __construct($options=[]) {
42
-        if (isset($options['randomness']) && is_callable($options['randomness'])) {
43
-            $this->randomness = $options['randomness'];
44
-        } else {
45
-            $this->randomness = function() {
46
-                return mt_rand() / mt_getrandmax();
47
-            };
48
-        }
49
-    }
50
-
51
-    /**
52
-     * Run the random encounter
53
-     *
54
-     * @note
55
-     * Currently only random enc. is an Oni attack! Yay! They take turns and a
56
-     * kill and do a little damage.
57
-     */
58
-    private function randomEncounter(Player $player) {
59
-        $oni_health_loss  = rand(1, self::ONI_DAMAGE_CAP);
60
-        $multiple_rewards = false;
61
-        $oni_killed       = false;
62
-        $item             = null;
63
-
64
-        $player->changeTurns(-1*self::ONI_TURN_LOSS);
65
-        $player->harm($oni_health_loss);
66
-        $player->subtractKills(self::ONI_KILL_LOSS);
67
-
68
-        if ($player->health > 0) { // if you survive
69
-            $inventory = new Inventory($player);
70
-
71
-            if ($player->turns > self::HIGH_TURNS) { // And your turns are high/you are energetic, you can kill them.
72
-                $oni_killed       = true;
73
-                $item             = Item::findByIdentity('dimmak');
74
-                $quantity         = 1;
75
-                $inventory->add($item->identity(), $quantity);
76
-            } else if ($player->turns > floor(self::HIGH_TURNS/2) && rand()&1) {
77
-                // If your turns are somewhat high/you have some energy, 50/50 chance you can kill them.
78
-                $oni_killed       = true;
79
-                $item             = Item::findByIdentity('ginsengroot');
80
-                $multiple_rewards = true;
81
-                $quantity         = 4;
82
-                $inventory->add($item->identity(), $quantity);
83
-            }
84
-        }
85
-
86
-        $player->save();
87
-
88
-        return [
89
-            'npc.oni.tpl',
90
-            [
91
-                'victory'          => $oni_killed,
92
-                'item'             => $item,
93
-                'multiple_rewards' => $multiple_rewards,
94
-            ],
95
-        ];
96
-    }
97
-
98
-    /**
99
-     * Wrapper for session storage of thief attacking
100
-     *
101
-     * @param Container
102
-     * @return int
103
-     */
104
-    private function getThiefCounter(Container $p_dependencies) {
105
-        return $p_dependencies['session']->get('thief_counter', 1);
106
-    }
107
-
108
-    /**
109
-     * Wrapper for session storage of thief attacking
110
-     *
111
-     * @param int $num
112
-     * @param Container
113
-     * @return void
114
-     */
115
-    private function setThiefCounter($num, Container $p_dependencies) {
116
-        $p_dependencies['session']->set('thief_counter', $num);
117
-    }
118
-
119
-    /**
120
-     * The reward for defeating an npc, less if items popped
121
-     *
122
-     * @param Npc $npco
123
-     * @param boolean $reward_item Any items were rewarded.
124
-     * @return int
125
-     * @note
126
-     * If npc gold explicitly set to 0, reward gold will be totally skipped
127
-     * "rich" npcs will have a higher gold minimum
128
-     */
129
-    private function calcReceivedGold(Npc $npco, $reward_item) {
130
-        if ($npco->gold() === 0) { // These npcs simply don't give gold.
131
-            return 0;
132
-        }
133
-
134
-        // Hack a little off max gold if items received.
135
-        $divisor = 1;
136
-        if ($reward_item) {
137
-            $divisor = self::ITEM_DECREASES_GOLD_DIVISOR;
138
-        }
139
-
140
-        return rand($npco->minGold(), floor($npco->gold()/$divisor));
141
-    }
142
-
143
-    /**
144
-     * Handle Standard Abstract Npcs
145
-     *
146
-     * @param String $victim
147
-     * @param Player $player
148
-     * @param Array $npcs
149
-     * @return array [$npc_template, $combat_data]
150
-     */
151
-    private function attackAbstractNpc($victim, Player $player, $npcs) {
152
-        $npc_stats        = $npcs[$victim]; // Pull an npcs individual stats with generic fallbacks.
153
-        $npco             = new Npc($npc_stats); // Construct the npc object.
154
-        $display_name     = (isset($npc_stats['name']) ? $npc_stats['name'] : ucfirst($victim));
155
-        $status_effect    = (isset($npc_stats['status']) ? $npc_stats['status'] : null);
156
-        $reward_item      = (isset($npc_stats['item']) && $npc_stats['item'] ? $npc_stats['item'] : null);
157
-        $is_quick         = (boolean) ($npco->getSpeed() > $player->getSpeed()); // Beyond basic speed and they see you coming, so show that message.
158
-        $is_weaker        = ($npco->getStrength() * 3) < $player->getStrength(); // Npc much weaker?
159
-        $is_stronger      = ($npco->getStrength()) > ($player->getStrength() * 3); // Npc More than twice as strong?
160
-        $image            = (isset($npc_stats['img']) ? $npc_stats['img'] : null);
161
-        // Assume defeat...
162
-        $victory          = false;
163
-        $received_gold    = null;
164
-        $received_items   = null;
165
-        $added_bounty     = '';
166
-        $is_rewarded      = null; // Gets items or gold.
167
-        $statuses         = null;
168
-        $status_classes   = null;
169
-        $image_path       = null;
170
-
171
-        // If the image exists, set the path to it for use on the page.
172
-        if ($image && file_exists(SERVER_ROOT.'www/images/characters/'.$image)) {
173
-            $image_path = IMAGE_ROOT.'characters/'.$image;
174
-        }
175
-
176
-        // ******* FIGHT Logic ***********
177
-        $npc_damage    = $npco->damage();
178
-        $survive_fight = $player->harm($npc_damage);
179
-        $kill_npc      = ($npco->getHealth() < $player->damage());
180
-
181
-        if ($survive_fight > 0) {
182
-            // The ninja survived, they get any gold the npc has.
183
-            $received_gold = $this->calcReceivedGold($npco, (bool) $reward_item);
184
-            $player->setGold($player->gold + $received_gold);
185
-            $received_items = array();
186
-
187
-            if ($kill_npc) {
188
-                $victory = true;
189
-                // Victory occurred, reward the poor sap.
190
-                if ($npco->inventory()) {
191
-                    $inventory = new Inventory($player);
192
-
193
-                    foreach (array_keys($npco->inventory()) as $l_item) {
194
-                        $item = Item::findByIdentity($l_item);
195
-                        $received_items[] = $item->getName();
196
-                        $inventory->add($item->identity(), 1);
197
-                    }
198
-                }
199
-
200
-                // Add bounty where applicable for npcs.
201
-                if ($npco->bountyMod() > 0 &&
202
-                    $player->level > self::MIN_LEVEL_FOR_BOUNTY &&
203
-                    $player->level <= self::MAX_LEVEL_FOR_BOUNTY
204
-                ) {
205
-                    $added_bounty = Combat::runBountyExchange($player, $npco, $npco->bountyMod());
206
-                }
207
-            }
208
-
209
-            $is_rewarded = (bool) $received_gold || (bool)count($received_items);
210
-
211
-            if (isset($npc_stats['status']) && null !== $npc_stats['status']) {
212
-                $player->addStatus($npc_stats['status']);
213
-                // Get the statuses and status classes for display.
214
-                $statuses = implode(', ', Player::getStatusList());
215
-                $status_classes = implode(' ', Player::getStatusList());
216
-            }
217
-        }
218
-
219
-        $player->save();
220
-
221
-        return [
222
-            'npc.abstract.tpl',
223
-            [
224
-                'victim'                   => $victim,
225
-                'display_name'             => $display_name,
226
-                'attack_damage'            => $npc_damage,
227
-                'status_effect'            => $status_effect,
228
-                'display_statuses'         => $statuses,
229
-                'display_statuses_classes' => $status_classes,
230
-                'received_gold'            => $received_gold,
231
-                'received_display_items'   => $received_items,
232
-                'is_rewarded'              => $is_rewarded,
233
-                'victory'                  => $victory,
234
-                'survive_fight'            => $survive_fight,
235
-                'kill_npc'                 => $kill_npc,
236
-                'image_path'               => $image_path,
237
-                'npc_stats'                => $npc_stats,
238
-                'is_quick'                 => $is_quick,
239
-                'added_bounty'             => $added_bounty,
240
-                'is_villager'              => $npco->hasTrait('villager'),
241
-                'race'                     => $npco->race(),
242
-                'is_weaker'                => $is_weaker,
243
-                'is_stronger'              => $is_stronger,
244
-            ]
245
-        ];
246
-    }
247
-
248
-    /**
249
-     * Injectable randomness.
250
-     *
251
-     * @return boolean
252
-     * @note
253
-     * Used to be rand(1, 400) === 1
254
-     */
255
-    private function startRandomEncounter() {
256
-        $randomness = $this->randomness;
257
-        return (boolean) (ceil($randomness() * self::RANDOM_ENCOUNTER_DIVISOR) == self::RANDOM_ENCOUNTER_DIVISOR);
258
-    }
259
-
260
-    /**
261
-     * Attack a specific npc
262
-     *
263
-     * @param Container
264
-     * @return StreamedViewResponse
265
-     */
266
-    public function attack(Container $p_dependencies) {
267
-        $request = RequestWrapper::$request;
268
-
269
-        $url_part = $request->getRequestUri();
270
-
271
-        if (preg_match('#\/(\w+)(\/)?$#', $url_part, $matches)) {
272
-            $victim = $matches[1];
273
-        } else {
274
-            $victim = null; // No match, victim is null.
275
-        }
276
-
277
-        $turn_cost      = 1;
278
-        $health         = true;
279
-        $combat_data    = [];
280
-        $player         = $p_dependencies['current_player'];
281
-        $error_template = 'npc.no-one.tpl'; // Error template also used down below.
282
-        $npc_template   = $error_template; // Error condition by default.
283
-        $npcs           = NpcFactory::npcsData();
284
-        $possible_npcs  = array_merge(array_column(NpcFactory::customNpcs(), 'identity'), array_keys($npcs));
285
-        $victim         = (in_array($victim, $possible_npcs) ? $victim : null); // Filter to only the correct options.
286
-
287
-        $standard_npcs  = [
288
-            'peasant'  => 'attackVillager',
289
-            'merchant' => 'attackMerchant',
290
-            'guard'    => 'attackGuard',
291
-        ];
292
-
293
-        $method = null;
294
-
295
-        if ($player && $player->turns > 0 && !empty($victim)) {
296
-            // Strip stealth when attacking special NPCs
297
-            if ($player->hasStatus('stealth') && in_array(strtolower($victim), self::$STEALTH_REMOVING_NPCS)) {
298
-                $player->subtractStatus(STEALTH);
299
-            }
300
-
301
-            if ($this->startRandomEncounter()) {
302
-                $method = 'randomEncounter';
303
-            } elseif (array_key_exists($victim, $npcs)) {
304
-                list($npc_template, $combat_data) = $this->attackAbstractNpc($victim, $player, $npcs);
305
-            } else if (array_key_exists($victim, $standard_npcs)) {
306
-                $method = $standard_npcs[$victim];
307
-            } else if ($victim == "samurai") {
308
-                if ($player->level < 2) {
309
-                    $turn_cost = 0;
310
-                    $npc_template = 'npc.samurai-too-weak.tpl';
311
-                } else if ($player->kills < 1) {
312
-                    $turn_cost = 0;
313
-                    $npc_template = 'npc.samurai-too-tired.tpl';
314
-                } else {
315
-                    $method = 'attackSamurai';
316
-                }
317
-            } else if ($victim == 'thief') {
318
-                // Check the counter to see whether they've attacked a thief multiple times in a row.
319
-                $counter = $this->getThiefCounter($p_dependencies);
320
-
321
-                $this->setThiefCounter($counter+1, $p_dependencies); // Incremement the current state of the counter.
322
-
323
-                if ($counter > 20 && rand(1, 3) == 3) {
324
-                    // Only after many attacks do you have the chance to be attacked back by the group of thieves.
325
-                    $this->setThiefCounter(0, $p_dependencies); // Reset the counter to zero.
326
-                    $method = 'attackGroupOfThieves';
327
-                } else {
328
-                    $method = 'attackNormalThief';
329
-                }
330
-            }
331
-
332
-            if (is_callable([$this, $method], false)) {
333
-                list($npc_template, $combat_data) = $this->$method($player);
334
-            }
335
-
336
-            if ($player->health <= 0) { // FINAL CHECK FOR DEATH
337
-                $player->death();
338
-                $health = false;
339
-                Event::create((int)"SysMsg", $player->id(), "DEATH: You have been killed by a $victim.");
340
-            }
341
-
342
-            // Subtract the turn cost for attacking an npc
343
-            // almost always 1 apart from perhaps oni or group-of-thieves
344
-            $player->changeTurns(-1*$turn_cost);
345
-
346
-            $player->save();
347
-        }
348
-
349
-        // Uses a sub-template inside for specific npcs.
350
-        $parts = [
351
-            'victim'       => $victim, // merge may override in theory
352
-            'npc_template' => $npc_template,
353
-            'attacked'     => 1,
354
-            'turns'        => $player? $player->turns : null,
355
-            'health'       => $health,
356
-        ];
357
-
358
-        return new StreamedViewResponse('Battle', 'npc.tpl', $parts + $combat_data, ['quickstat' => 'player']);
359
-    }
360
-
361
-    private function attackGuard(Player $player) {
362
-        $damage = rand(1, $player->getStrength() + 10);
363
-        $herb   = false;
364
-        $gold   = 0;
365
-        $bounty = 0;
366
-
367
-        if ($victory = $player->harm($damage)) {
368
-            $gold = rand(1, $player->getStrength() + 40);
369
-            $player->setGold($player->gold + $gold);
370
-
371
-            if ($player->level > 15) {
372
-                $bounty = 10 * floor(($player->level - 10) / 5);
373
-                $player->setBounty($player->bounty + $bounty);
374
-            }
375
-
376
-            // chance of getting an herb for Kampo
377
-            if (rand(1, 9) == 9) {
378
-                $herb = true;
379
-                $inventory = new Inventory($player);
380
-                $inventory->add('ginsengroot', 1);
381
-            }
382
-        } else {
383
-            $damage = 0;
384
-        }
385
-
386
-        return [
387
-            'npc.guard.tpl',
388
-            [
389
-                'attack'  => $damage,
390
-                'gold'    => $gold,
391
-                'bounty'  => $bounty,
392
-                'victory' => $victory,
393
-                'herb'    => $herb,
394
-            ],
395
-        ];
396
-    }
397
-
398
-    private function attackVillager(Player $player) {
399
-        $damage        = rand(0, 10);
400
-        $just_villager = rand(0, 20);
401
-        $bounty        = 0;
402
-        $gold          = 0;
403
-
404
-        if ($victory = $player->harm($damage)) {
405
-            $gold = rand(0, 20);
406
-            $player->setGold($player->gold + $gold);
407
-
408
-            // *** Bounty or no bounty ***
409
-            if ($player->level > 1 && $player->level <= 20) {
410
-                $bounty = floor($player->level / 3);
411
-                $player->setBounty($player->bounty + $bounty);
412
-            }
413
-
414
-            if (!$just_villager) {
415
-                // Something beyond just a villager, drop a shuriken
416
-                $inventory = new Inventory($player);
417
-                $inventory->add('shuriken', 1);
418
-            }
419
-        }
420
-
421
-        $player->save();
422
-
423
-        return [
424
-            'npc.peasant.tpl',
425
-            [
426
-                'just_villager' => $just_villager,
427
-                'attack'        => $damage,
428
-                'gold'          => $gold,
429
-                'level'         => $player->level,
430
-                'bounty'        => $bounty,
431
-                'victory'       => $victory,
432
-            ],
433
-        ];
434
-    }
435
-
436
-    private function attackSamurai(Player $player) {
437
-        $gold         = 0;
438
-        $victory      = false;
439
-        $drop         = false;
440
-        $drop_display = null;
441
-
442
-        $damage = [
443
-            rand(1, $player->getStrength()),
444
-            rand(10, 10 + round($player->getStrength() * 1.2)),
445
-        ];
446
-
447
-        if (rand(0, 1)) {
448
-            $damage[] = rand(30 + round($player->getStrength() * 0.2), 30 + round($player->getStrength() * 1.7));
449
-        } else { //Instant death.
450
-            $damage[] = abs($player->health - $damage[0] - $damage[1]);
451
-        }
452
-
453
-        for ($i = 0; $i < count($damage) && $player->health > 0; ++$i) {
454
-            $player->harm($damage[$i]);
455
-        }
456
-
457
-        if ($player->health > 0) { // Ninja still has health after all attacks
458
-            $victory = true;
459
-
460
-            $gold = rand(50, 50 + $damage[2] + $damage[1]);
461
-
462
-            $player->addKills(1);
463
-            $player->setGold($player->gold + $gold);
464
-
465
-            $inventory = new Inventory($player);
466
-
467
-            // If samurai dmg high, but ninja lived, give rewards
468
-            if ($damage[2] > self::SAMURAI_REWARD_DMG) {
469
-                $drop = true;
470
-
471
-                if (rand(0, 1)) {
472
-                    $drop_display = 'mushroom powder';
473
-                    $dropItem = 'amanita';
474
-                } else {
475
-                    $drop_display = 'a strange herb';
476
-                    $dropItem = 'ginsengroot';
477
-                }
478
-
479
-                $inventory->add($dropItem, 1);
480
-            }
481
-
482
-            // If the final damage was the exact max damage
483
-            if ($damage[2] == $player->getStrength() * 3) {
484
-                $drop         = true;
485
-                $drop_display = 'a black scroll';
486
-                $inventory->add('dimmak', 1);
487
-            }
488
-        }
489
-
490
-        $player->save();
491
-
492
-        return [
493
-            'npc.samurai.tpl',
494
-            [
495
-                'samurai_damage_array' => $damage,
496
-                'gold'                 => $gold,
497
-                'victory'              => $victory,
498
-                'ninja_str'            => $player->getStrength(),
499
-                'level'                => $player->level,
500
-                'attacker_kills'       => $player->kills,
501
-                'drop'                 => $drop,
502
-                'drop_display'         => $drop_display,
503
-            ],
504
-        ];
505
-    }
506
-
507
-    private function attackGroupOfThieves(Player $player) {
508
-        $damage = rand(50, 150);
509
-
510
-        if ($victory = $player->harm($damage)) {
511
-            // The den of thieves didn't accomplish their goal
512
-            $gold = rand(100, 300);
513
-
514
-            if ($damage > 120) { // Powerful attack gives an additional disadvantage
515
-                $player->subtractKills(1);
516
-            }
517
-
518
-            $player->setGold($player->gold + $gold);
519
-
520
-            $inventory = new Inventory($player);
521
-            $inventory->add('phosphor', 1);
522
-        } else {    // If the den of theives killed the attacker.
523
-            $gold = 0;
524
-        }
525
-
526
-        $player->save();
527
-
528
-        return [
529
-            'npc.thief-group.tpl',
530
-            [
531
-                'attack'  => $damage,
532
-                'gold'    => $gold,
533
-                'victory' => $victory,
534
-            ],
535
-        ];
536
-    }
537
-
538
-    /**
539
-     * Attack merchant
540
-     */
541
-    private function attackMerchant($player) {
542
-        $damage = rand(15, 35);
543
-        $bounty = 0;
544
-
545
-        // Player killed NPC
546
-        if ($victory = $player->harm($damage)) {
547
-            $gold = rand(20, 70);
548
-            $player->setGold($player->gold + $gold);
549
-
550
-            if ($damage > 34) {
551
-                $inventory = new Inventory($player);
552
-                $inventory->add('phosphor', 1);
553
-            }
554
-
555
-            if ($player->level > 10) {
556
-                $bounty = 5 * floor(($player->level - 5) / 3);
557
-                $player->setBounty($player->bounty + $bounty);
558
-            }
559
-        } else { // NPC killed player
560
-            $damage = $gold = 0;
561
-        }
562
-
563
-        $player->save();
564
-
565
-        return [
566
-            'npc.merchant.tpl',
567
-            [
568
-                'attack'  => $damage,
569
-                'gold'    => $gold,
570
-                'bounty'  => $bounty,
571
-                'victory' => $victory,
572
-            ],
573
-        ];
574
-    }
575
-
576
-    /**
577
-     * Normal attack on a single thief.
578
-     */
579
-    private function attackNormalThief(Player $player) {
580
-        $damage = rand(0, 35);  // Damage done
581
-        $gold   = 0;
582
-
583
-        if ($victory = $player->harm($damage)) {
584
-            $gold = rand(0, 40);  // Gold in question
585
-
586
-            if ($damage > 30) { // Steal gold
587
-                $player->setGold(max(0, $player->gold - $gold));
588
-            } else if ($damage < 30) { // award gold and item
589
-                $player->setGold($player->gold + $gold);
590
-                $inventory = new Inventory($player);
591
-                $inventory->add('shuriken', 1);
592
-            }
593
-        }
594
-
595
-        $player->save();
596
-
597
-        return [
598
-            'npc.thief.tpl',
599
-            [
600
-                'attack'  => $damage,
601
-                'gold'    => $gold,
602
-                'victory' => $victory,
603
-            ],
604
-        ];
605
-    }
606
-
607
-    /**
608
-     * Obtain the npcs data.
609
-     *
610
-     * @return Array
611
-     */
612
-    private function npcs() {
613
-        return [
614
-            'abstract_npcs' => NpcFactory::npcsData(),
615
-            'custom_npcs'   => NpcFactory::customNpcs(),
616
-        ];
617
-    }
618
-
619
-    /**
620
-     * Get the list of npcs in a subtemplate.
621
-     *
622
-     * @param Container
623
-     * @return Response
624
-     */
625
-    public function index(Container $p_dependencies) {
626
-        $all_npcs   = $this->npcs();
627
-        $other_npcs = $all_npcs['abstract_npcs'];
628
-        $npcs       = $all_npcs['custom_npcs'];
629
-        $template   = 'npc.list.tpl';
630
-        $title      = 'Npcs';
631
-        $parts      = ['npcs' => $npcs, 'other_npcs' => $other_npcs];
632
-        $options    = ['quickstats' => 'player'];
633
-
634
-        return new StreamedViewResponse($title, $template, $parts, $options);
635
-    }
22
+	const ALIVE                      = true;
23
+	const PRIV                       = false;
24
+	const HIGH_TURNS                 = 50;
25
+	const ITEM_DECREASES_GOLD_DIVISOR = 1.11;
26
+	const ONI_DAMAGE_CAP             = 20;
27
+	const RANDOM_ENCOUNTER_DIVISOR   = 400;
28
+	const SAMURAI_REWARD_DMG         = 100;
29
+	const ONI_TURN_LOSS              = 10;
30
+	const ONI_KILL_LOSS              = 1;
31
+	const MIN_LEVEL_FOR_BOUNTY       = 5;
32
+	const MAX_LEVEL_FOR_BOUNTY       = 50;
33
+
34
+	public static $STEALTH_REMOVING_NPCS = ['samurai', 'oni'];
35
+
36
+	private $randomness = null;
37
+
38
+	/**
39
+	 * Inject different seed when non-randomness is needed (for testing)
40
+	 */
41
+	public function __construct($options=[]) {
42
+		if (isset($options['randomness']) && is_callable($options['randomness'])) {
43
+			$this->randomness = $options['randomness'];
44
+		} else {
45
+			$this->randomness = function() {
46
+				return mt_rand() / mt_getrandmax();
47
+			};
48
+		}
49
+	}
50
+
51
+	/**
52
+	 * Run the random encounter
53
+	 *
54
+	 * @note
55
+	 * Currently only random enc. is an Oni attack! Yay! They take turns and a
56
+	 * kill and do a little damage.
57
+	 */
58
+	private function randomEncounter(Player $player) {
59
+		$oni_health_loss  = rand(1, self::ONI_DAMAGE_CAP);
60
+		$multiple_rewards = false;
61
+		$oni_killed       = false;
62
+		$item             = null;
63
+
64
+		$player->changeTurns(-1*self::ONI_TURN_LOSS);
65
+		$player->harm($oni_health_loss);
66
+		$player->subtractKills(self::ONI_KILL_LOSS);
67
+
68
+		if ($player->health > 0) { // if you survive
69
+			$inventory = new Inventory($player);
70
+
71
+			if ($player->turns > self::HIGH_TURNS) { // And your turns are high/you are energetic, you can kill them.
72
+				$oni_killed       = true;
73
+				$item             = Item::findByIdentity('dimmak');
74
+				$quantity         = 1;
75
+				$inventory->add($item->identity(), $quantity);
76
+			} else if ($player->turns > floor(self::HIGH_TURNS/2) && rand()&1) {
77
+				// If your turns are somewhat high/you have some energy, 50/50 chance you can kill them.
78
+				$oni_killed       = true;
79
+				$item             = Item::findByIdentity('ginsengroot');
80
+				$multiple_rewards = true;
81
+				$quantity         = 4;
82
+				$inventory->add($item->identity(), $quantity);
83
+			}
84
+		}
85
+
86
+		$player->save();
87
+
88
+		return [
89
+			'npc.oni.tpl',
90
+			[
91
+				'victory'          => $oni_killed,
92
+				'item'             => $item,
93
+				'multiple_rewards' => $multiple_rewards,
94
+			],
95
+		];
96
+	}
97
+
98
+	/**
99
+	 * Wrapper for session storage of thief attacking
100
+	 *
101
+	 * @param Container
102
+	 * @return int
103
+	 */
104
+	private function getThiefCounter(Container $p_dependencies) {
105
+		return $p_dependencies['session']->get('thief_counter', 1);
106
+	}
107
+
108
+	/**
109
+	 * Wrapper for session storage of thief attacking
110
+	 *
111
+	 * @param int $num
112
+	 * @param Container
113
+	 * @return void
114
+	 */
115
+	private function setThiefCounter($num, Container $p_dependencies) {
116
+		$p_dependencies['session']->set('thief_counter', $num);
117
+	}
118
+
119
+	/**
120
+	 * The reward for defeating an npc, less if items popped
121
+	 *
122
+	 * @param Npc $npco
123
+	 * @param boolean $reward_item Any items were rewarded.
124
+	 * @return int
125
+	 * @note
126
+	 * If npc gold explicitly set to 0, reward gold will be totally skipped
127
+	 * "rich" npcs will have a higher gold minimum
128
+	 */
129
+	private function calcReceivedGold(Npc $npco, $reward_item) {
130
+		if ($npco->gold() === 0) { // These npcs simply don't give gold.
131
+			return 0;
132
+		}
133
+
134
+		// Hack a little off max gold if items received.
135
+		$divisor = 1;
136
+		if ($reward_item) {
137
+			$divisor = self::ITEM_DECREASES_GOLD_DIVISOR;
138
+		}
139
+
140
+		return rand($npco->minGold(), floor($npco->gold()/$divisor));
141
+	}
142
+
143
+	/**
144
+	 * Handle Standard Abstract Npcs
145
+	 *
146
+	 * @param String $victim
147
+	 * @param Player $player
148
+	 * @param Array $npcs
149
+	 * @return array [$npc_template, $combat_data]
150
+	 */
151
+	private function attackAbstractNpc($victim, Player $player, $npcs) {
152
+		$npc_stats        = $npcs[$victim]; // Pull an npcs individual stats with generic fallbacks.
153
+		$npco             = new Npc($npc_stats); // Construct the npc object.
154
+		$display_name     = (isset($npc_stats['name']) ? $npc_stats['name'] : ucfirst($victim));
155
+		$status_effect    = (isset($npc_stats['status']) ? $npc_stats['status'] : null);
156
+		$reward_item      = (isset($npc_stats['item']) && $npc_stats['item'] ? $npc_stats['item'] : null);
157
+		$is_quick         = (boolean) ($npco->getSpeed() > $player->getSpeed()); // Beyond basic speed and they see you coming, so show that message.
158
+		$is_weaker        = ($npco->getStrength() * 3) < $player->getStrength(); // Npc much weaker?
159
+		$is_stronger      = ($npco->getStrength()) > ($player->getStrength() * 3); // Npc More than twice as strong?
160
+		$image            = (isset($npc_stats['img']) ? $npc_stats['img'] : null);
161
+		// Assume defeat...
162
+		$victory          = false;
163
+		$received_gold    = null;
164
+		$received_items   = null;
165
+		$added_bounty     = '';
166
+		$is_rewarded      = null; // Gets items or gold.
167
+		$statuses         = null;
168
+		$status_classes   = null;
169
+		$image_path       = null;
170
+
171
+		// If the image exists, set the path to it for use on the page.
172
+		if ($image && file_exists(SERVER_ROOT.'www/images/characters/'.$image)) {
173
+			$image_path = IMAGE_ROOT.'characters/'.$image;
174
+		}
175
+
176
+		// ******* FIGHT Logic ***********
177
+		$npc_damage    = $npco->damage();
178
+		$survive_fight = $player->harm($npc_damage);
179
+		$kill_npc      = ($npco->getHealth() < $player->damage());
180
+
181
+		if ($survive_fight > 0) {
182
+			// The ninja survived, they get any gold the npc has.
183
+			$received_gold = $this->calcReceivedGold($npco, (bool) $reward_item);
184
+			$player->setGold($player->gold + $received_gold);
185
+			$received_items = array();
186
+
187
+			if ($kill_npc) {
188
+				$victory = true;
189
+				// Victory occurred, reward the poor sap.
190
+				if ($npco->inventory()) {
191
+					$inventory = new Inventory($player);
192
+
193
+					foreach (array_keys($npco->inventory()) as $l_item) {
194
+						$item = Item::findByIdentity($l_item);
195
+						$received_items[] = $item->getName();
196
+						$inventory->add($item->identity(), 1);
197
+					}
198
+				}
199
+
200
+				// Add bounty where applicable for npcs.
201
+				if ($npco->bountyMod() > 0 &&
202
+					$player->level > self::MIN_LEVEL_FOR_BOUNTY &&
203
+					$player->level <= self::MAX_LEVEL_FOR_BOUNTY
204
+				) {
205
+					$added_bounty = Combat::runBountyExchange($player, $npco, $npco->bountyMod());
206
+				}
207
+			}
208
+
209
+			$is_rewarded = (bool) $received_gold || (bool)count($received_items);
210
+
211
+			if (isset($npc_stats['status']) && null !== $npc_stats['status']) {
212
+				$player->addStatus($npc_stats['status']);
213
+				// Get the statuses and status classes for display.
214
+				$statuses = implode(', ', Player::getStatusList());
215
+				$status_classes = implode(' ', Player::getStatusList());
216
+			}
217
+		}
218
+
219
+		$player->save();
220
+
221
+		return [
222
+			'npc.abstract.tpl',
223
+			[
224
+				'victim'                   => $victim,
225
+				'display_name'             => $display_name,
226
+				'attack_damage'            => $npc_damage,
227
+				'status_effect'            => $status_effect,
228
+				'display_statuses'         => $statuses,
229
+				'display_statuses_classes' => $status_classes,
230
+				'received_gold'            => $received_gold,
231
+				'received_display_items'   => $received_items,
232
+				'is_rewarded'              => $is_rewarded,
233
+				'victory'                  => $victory,
234
+				'survive_fight'            => $survive_fight,
235
+				'kill_npc'                 => $kill_npc,
236
+				'image_path'               => $image_path,
237
+				'npc_stats'                => $npc_stats,
238
+				'is_quick'                 => $is_quick,
239
+				'added_bounty'             => $added_bounty,
240
+				'is_villager'              => $npco->hasTrait('villager'),
241
+				'race'                     => $npco->race(),
242
+				'is_weaker'                => $is_weaker,
243
+				'is_stronger'              => $is_stronger,
244
+			]
245
+		];
246
+	}
247
+
248
+	/**
249
+	 * Injectable randomness.
250
+	 *
251
+	 * @return boolean
252
+	 * @note
253
+	 * Used to be rand(1, 400) === 1
254
+	 */
255
+	private function startRandomEncounter() {
256
+		$randomness = $this->randomness;
257
+		return (boolean) (ceil($randomness() * self::RANDOM_ENCOUNTER_DIVISOR) == self::RANDOM_ENCOUNTER_DIVISOR);
258
+	}
259
+
260
+	/**
261
+	 * Attack a specific npc
262
+	 *
263
+	 * @param Container
264
+	 * @return StreamedViewResponse
265
+	 */
266
+	public function attack(Container $p_dependencies) {
267
+		$request = RequestWrapper::$request;
268
+
269
+		$url_part = $request->getRequestUri();
270
+
271
+		if (preg_match('#\/(\w+)(\/)?$#', $url_part, $matches)) {
272
+			$victim = $matches[1];
273
+		} else {
274
+			$victim = null; // No match, victim is null.
275
+		}
276
+
277
+		$turn_cost      = 1;
278
+		$health         = true;
279
+		$combat_data    = [];
280
+		$player         = $p_dependencies['current_player'];
281
+		$error_template = 'npc.no-one.tpl'; // Error template also used down below.
282
+		$npc_template   = $error_template; // Error condition by default.
283
+		$npcs           = NpcFactory::npcsData();
284
+		$possible_npcs  = array_merge(array_column(NpcFactory::customNpcs(), 'identity'), array_keys($npcs));
285
+		$victim         = (in_array($victim, $possible_npcs) ? $victim : null); // Filter to only the correct options.
286
+
287
+		$standard_npcs  = [
288
+			'peasant'  => 'attackVillager',
289
+			'merchant' => 'attackMerchant',
290
+			'guard'    => 'attackGuard',
291
+		];
292
+
293
+		$method = null;
294
+
295
+		if ($player && $player->turns > 0 && !empty($victim)) {
296
+			// Strip stealth when attacking special NPCs
297
+			if ($player->hasStatus('stealth') && in_array(strtolower($victim), self::$STEALTH_REMOVING_NPCS)) {
298
+				$player->subtractStatus(STEALTH);
299
+			}
300
+
301
+			if ($this->startRandomEncounter()) {
302
+				$method = 'randomEncounter';
303
+			} elseif (array_key_exists($victim, $npcs)) {
304
+				list($npc_template, $combat_data) = $this->attackAbstractNpc($victim, $player, $npcs);
305
+			} else if (array_key_exists($victim, $standard_npcs)) {
306
+				$method = $standard_npcs[$victim];
307
+			} else if ($victim == "samurai") {
308
+				if ($player->level < 2) {
309
+					$turn_cost = 0;
310
+					$npc_template = 'npc.samurai-too-weak.tpl';
311
+				} else if ($player->kills < 1) {
312
+					$turn_cost = 0;
313
+					$npc_template = 'npc.samurai-too-tired.tpl';
314
+				} else {
315
+					$method = 'attackSamurai';
316
+				}
317
+			} else if ($victim == 'thief') {
318
+				// Check the counter to see whether they've attacked a thief multiple times in a row.
319
+				$counter = $this->getThiefCounter($p_dependencies);
320
+
321
+				$this->setThiefCounter($counter+1, $p_dependencies); // Incremement the current state of the counter.
322
+
323
+				if ($counter > 20 && rand(1, 3) == 3) {
324
+					// Only after many attacks do you have the chance to be attacked back by the group of thieves.
325
+					$this->setThiefCounter(0, $p_dependencies); // Reset the counter to zero.
326
+					$method = 'attackGroupOfThieves';
327
+				} else {
328
+					$method = 'attackNormalThief';
329
+				}
330
+			}
331
+
332
+			if (is_callable([$this, $method], false)) {
333
+				list($npc_template, $combat_data) = $this->$method($player);
334
+			}
335
+
336
+			if ($player->health <= 0) { // FINAL CHECK FOR DEATH
337
+				$player->death();
338
+				$health = false;
339
+				Event::create((int)"SysMsg", $player->id(), "DEATH: You have been killed by a $victim.");
340
+			}
341
+
342
+			// Subtract the turn cost for attacking an npc
343
+			// almost always 1 apart from perhaps oni or group-of-thieves
344
+			$player->changeTurns(-1*$turn_cost);
345
+
346
+			$player->save();
347
+		}
348
+
349
+		// Uses a sub-template inside for specific npcs.
350
+		$parts = [
351
+			'victim'       => $victim, // merge may override in theory
352
+			'npc_template' => $npc_template,
353
+			'attacked'     => 1,
354
+			'turns'        => $player? $player->turns : null,
355
+			'health'       => $health,
356
+		];
357
+
358
+		return new StreamedViewResponse('Battle', 'npc.tpl', $parts + $combat_data, ['quickstat' => 'player']);
359
+	}
360
+
361
+	private function attackGuard(Player $player) {
362
+		$damage = rand(1, $player->getStrength() + 10);
363
+		$herb   = false;
364
+		$gold   = 0;
365
+		$bounty = 0;
366
+
367
+		if ($victory = $player->harm($damage)) {
368
+			$gold = rand(1, $player->getStrength() + 40);
369
+			$player->setGold($player->gold + $gold);
370
+
371
+			if ($player->level > 15) {
372
+				$bounty = 10 * floor(($player->level - 10) / 5);
373
+				$player->setBounty($player->bounty + $bounty);
374
+			}
375
+
376
+			// chance of getting an herb for Kampo
377
+			if (rand(1, 9) == 9) {
378
+				$herb = true;
379
+				$inventory = new Inventory($player);
380
+				$inventory->add('ginsengroot', 1);
381
+			}
382
+		} else {
383
+			$damage = 0;
384
+		}
385
+
386
+		return [
387
+			'npc.guard.tpl',
388
+			[
389
+				'attack'  => $damage,
390
+				'gold'    => $gold,
391
+				'bounty'  => $bounty,
392
+				'victory' => $victory,
393
+				'herb'    => $herb,
394
+			],
395
+		];
396
+	}
397
+
398
+	private function attackVillager(Player $player) {
399
+		$damage        = rand(0, 10);
400
+		$just_villager = rand(0, 20);
401
+		$bounty        = 0;
402
+		$gold          = 0;
403
+
404
+		if ($victory = $player->harm($damage)) {
405
+			$gold = rand(0, 20);
406
+			$player->setGold($player->gold + $gold);
407
+
408
+			// *** Bounty or no bounty ***
409
+			if ($player->level > 1 && $player->level <= 20) {
410
+				$bounty = floor($player->level / 3);
411
+				$player->setBounty($player->bounty + $bounty);
412
+			}
413
+
414
+			if (!$just_villager) {
415
+				// Something beyond just a villager, drop a shuriken
416
+				$inventory = new Inventory($player);
417
+				$inventory->add('shuriken', 1);
418
+			}
419
+		}
420
+
421
+		$player->save();
422
+
423
+		return [
424
+			'npc.peasant.tpl',
425
+			[
426
+				'just_villager' => $just_villager,
427
+				'attack'        => $damage,
428
+				'gold'          => $gold,
429
+				'level'         => $player->level,
430
+				'bounty'        => $bounty,
431
+				'victory'       => $victory,
432
+			],
433
+		];
434
+	}
435
+
436
+	private function attackSamurai(Player $player) {
437
+		$gold         = 0;
438
+		$victory      = false;
439
+		$drop         = false;
440
+		$drop_display = null;
441
+
442
+		$damage = [
443
+			rand(1, $player->getStrength()),
444
+			rand(10, 10 + round($player->getStrength() * 1.2)),
445
+		];
446
+
447
+		if (rand(0, 1)) {
448
+			$damage[] = rand(30 + round($player->getStrength() * 0.2), 30 + round($player->getStrength() * 1.7));
449
+		} else { //Instant death.
450
+			$damage[] = abs($player->health - $damage[0] - $damage[1]);
451
+		}
452
+
453
+		for ($i = 0; $i < count($damage) && $player->health > 0; ++$i) {
454
+			$player->harm($damage[$i]);
455
+		}
456
+
457
+		if ($player->health > 0) { // Ninja still has health after all attacks
458
+			$victory = true;
459
+
460
+			$gold = rand(50, 50 + $damage[2] + $damage[1]);
461
+
462
+			$player->addKills(1);
463
+			$player->setGold($player->gold + $gold);
464
+
465
+			$inventory = new Inventory($player);
466
+
467
+			// If samurai dmg high, but ninja lived, give rewards
468
+			if ($damage[2] > self::SAMURAI_REWARD_DMG) {
469
+				$drop = true;
470
+
471
+				if (rand(0, 1)) {
472
+					$drop_display = 'mushroom powder';
473
+					$dropItem = 'amanita';
474
+				} else {
475
+					$drop_display = 'a strange herb';
476
+					$dropItem = 'ginsengroot';
477
+				}
478
+
479
+				$inventory->add($dropItem, 1);
480
+			}
481
+
482
+			// If the final damage was the exact max damage
483
+			if ($damage[2] == $player->getStrength() * 3) {
484
+				$drop         = true;
485
+				$drop_display = 'a black scroll';
486
+				$inventory->add('dimmak', 1);
487
+			}
488
+		}
489
+
490
+		$player->save();
491
+
492
+		return [
493
+			'npc.samurai.tpl',
494
+			[
495
+				'samurai_damage_array' => $damage,
496
+				'gold'                 => $gold,
497
+				'victory'              => $victory,
498
+				'ninja_str'            => $player->getStrength(),
499
+				'level'                => $player->level,
500
+				'attacker_kills'       => $player->kills,
501
+				'drop'                 => $drop,
502
+				'drop_display'         => $drop_display,
503
+			],
504
+		];
505
+	}
506
+
507
+	private function attackGroupOfThieves(Player $player) {
508
+		$damage = rand(50, 150);
509
+
510
+		if ($victory = $player->harm($damage)) {
511
+			// The den of thieves didn't accomplish their goal
512
+			$gold = rand(100, 300);
513
+
514
+			if ($damage > 120) { // Powerful attack gives an additional disadvantage
515
+				$player->subtractKills(1);
516
+			}
517
+
518
+			$player->setGold($player->gold + $gold);
519
+
520
+			$inventory = new Inventory($player);
521
+			$inventory->add('phosphor', 1);
522
+		} else {    // If the den of theives killed the attacker.
523
+			$gold = 0;
524
+		}
525
+
526
+		$player->save();
527
+
528
+		return [
529
+			'npc.thief-group.tpl',
530
+			[
531
+				'attack'  => $damage,
532
+				'gold'    => $gold,
533
+				'victory' => $victory,
534
+			],
535
+		];
536
+	}
537
+
538
+	/**
539
+	 * Attack merchant
540
+	 */
541
+	private function attackMerchant($player) {
542
+		$damage = rand(15, 35);
543
+		$bounty = 0;
544
+
545
+		// Player killed NPC
546
+		if ($victory = $player->harm($damage)) {
547
+			$gold = rand(20, 70);
548
+			$player->setGold($player->gold + $gold);
549
+
550
+			if ($damage > 34) {
551
+				$inventory = new Inventory($player);
552
+				$inventory->add('phosphor', 1);
553
+			}
554
+
555
+			if ($player->level > 10) {
556
+				$bounty = 5 * floor(($player->level - 5) / 3);
557
+				$player->setBounty($player->bounty + $bounty);
558
+			}
559
+		} else { // NPC killed player
560
+			$damage = $gold = 0;
561
+		}
562
+
563
+		$player->save();
564
+
565
+		return [
566
+			'npc.merchant.tpl',
567
+			[
568
+				'attack'  => $damage,
569
+				'gold'    => $gold,
570
+				'bounty'  => $bounty,
571
+				'victory' => $victory,
572
+			],
573
+		];
574
+	}
575
+
576
+	/**
577
+	 * Normal attack on a single thief.
578
+	 */
579
+	private function attackNormalThief(Player $player) {
580
+		$damage = rand(0, 35);  // Damage done
581
+		$gold   = 0;
582
+
583
+		if ($victory = $player->harm($damage)) {
584
+			$gold = rand(0, 40);  // Gold in question
585
+
586
+			if ($damage > 30) { // Steal gold
587
+				$player->setGold(max(0, $player->gold - $gold));
588
+			} else if ($damage < 30) { // award gold and item
589
+				$player->setGold($player->gold + $gold);
590
+				$inventory = new Inventory($player);
591
+				$inventory->add('shuriken', 1);
592
+			}
593
+		}
594
+
595
+		$player->save();
596
+
597
+		return [
598
+			'npc.thief.tpl',
599
+			[
600
+				'attack'  => $damage,
601
+				'gold'    => $gold,
602
+				'victory' => $victory,
603
+			],
604
+		];
605
+	}
606
+
607
+	/**
608
+	 * Obtain the npcs data.
609
+	 *
610
+	 * @return Array
611
+	 */
612
+	private function npcs() {
613
+		return [
614
+			'abstract_npcs' => NpcFactory::npcsData(),
615
+			'custom_npcs'   => NpcFactory::customNpcs(),
616
+		];
617
+	}
618
+
619
+	/**
620
+	 * Get the list of npcs in a subtemplate.
621
+	 *
622
+	 * @param Container
623
+	 * @return Response
624
+	 */
625
+	public function index(Container $p_dependencies) {
626
+		$all_npcs   = $this->npcs();
627
+		$other_npcs = $all_npcs['abstract_npcs'];
628
+		$npcs       = $all_npcs['custom_npcs'];
629
+		$template   = 'npc.list.tpl';
630
+		$title      = 'Npcs';
631
+		$parts      = ['npcs' => $npcs, 'other_npcs' => $other_npcs];
632
+		$options    = ['quickstats' => 'player'];
633
+
634
+		return new StreamedViewResponse($title, $template, $parts, $options);
635
+	}
636 636
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -38,7 +38,7 @@  discard block
 block discarded – undo
38 38
     /**
39 39
      * Inject different seed when non-randomness is needed (for testing)
40 40
      */
41
-    public function __construct($options=[]) {
41
+    public function __construct($options = []) {
42 42
         if (isset($options['randomness']) && is_callable($options['randomness'])) {
43 43
             $this->randomness = $options['randomness'];
44 44
         } else {
@@ -61,7 +61,7 @@  discard block
 block discarded – undo
61 61
         $oni_killed       = false;
62 62
         $item             = null;
63 63
 
64
-        $player->changeTurns(-1*self::ONI_TURN_LOSS);
64
+        $player->changeTurns(-1 * self::ONI_TURN_LOSS);
65 65
         $player->harm($oni_health_loss);
66 66
         $player->subtractKills(self::ONI_KILL_LOSS);
67 67
 
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
                 $item             = Item::findByIdentity('dimmak');
74 74
                 $quantity         = 1;
75 75
                 $inventory->add($item->identity(), $quantity);
76
-            } else if ($player->turns > floor(self::HIGH_TURNS/2) && rand()&1) {
76
+            } else if ($player->turns > floor(self::HIGH_TURNS / 2) && rand() & 1) {
77 77
                 // If your turns are somewhat high/you have some energy, 50/50 chance you can kill them.
78 78
                 $oni_killed       = true;
79 79
                 $item             = Item::findByIdentity('ginsengroot');
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
             $divisor = self::ITEM_DECREASES_GOLD_DIVISOR;
138 138
         }
139 139
 
140
-        return rand($npco->minGold(), floor($npco->gold()/$divisor));
140
+        return rand($npco->minGold(), floor($npco->gold() / $divisor));
141 141
     }
142 142
 
143 143
     /**
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
         $display_name     = (isset($npc_stats['name']) ? $npc_stats['name'] : ucfirst($victim));
155 155
         $status_effect    = (isset($npc_stats['status']) ? $npc_stats['status'] : null);
156 156
         $reward_item      = (isset($npc_stats['item']) && $npc_stats['item'] ? $npc_stats['item'] : null);
157
-        $is_quick         = (boolean) ($npco->getSpeed() > $player->getSpeed()); // Beyond basic speed and they see you coming, so show that message.
157
+        $is_quick         = (boolean)($npco->getSpeed() > $player->getSpeed()); // Beyond basic speed and they see you coming, so show that message.
158 158
         $is_weaker        = ($npco->getStrength() * 3) < $player->getStrength(); // Npc much weaker?
159 159
         $is_stronger      = ($npco->getStrength()) > ($player->getStrength() * 3); // Npc More than twice as strong?
160 160
         $image            = (isset($npc_stats['img']) ? $npc_stats['img'] : null);
@@ -180,7 +180,7 @@  discard block
 block discarded – undo
180 180
 
181 181
         if ($survive_fight > 0) {
182 182
             // The ninja survived, they get any gold the npc has.
183
-            $received_gold = $this->calcReceivedGold($npco, (bool) $reward_item);
183
+            $received_gold = $this->calcReceivedGold($npco, (bool)$reward_item);
184 184
             $player->setGold($player->gold + $received_gold);
185 185
             $received_items = array();
186 186
 
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
                 }
207 207
             }
208 208
 
209
-            $is_rewarded = (bool) $received_gold || (bool)count($received_items);
209
+            $is_rewarded = (bool)$received_gold || (bool)count($received_items);
210 210
 
211 211
             if (isset($npc_stats['status']) && null !== $npc_stats['status']) {
212 212
                 $player->addStatus($npc_stats['status']);
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
      */
255 255
     private function startRandomEncounter() {
256 256
         $randomness = $this->randomness;
257
-        return (boolean) (ceil($randomness() * self::RANDOM_ENCOUNTER_DIVISOR) == self::RANDOM_ENCOUNTER_DIVISOR);
257
+        return (boolean)(ceil($randomness() * self::RANDOM_ENCOUNTER_DIVISOR) == self::RANDOM_ENCOUNTER_DIVISOR);
258 258
     }
259 259
 
260 260
     /**
@@ -318,7 +318,7 @@  discard block
 block discarded – undo
318 318
                 // Check the counter to see whether they've attacked a thief multiple times in a row.
319 319
                 $counter = $this->getThiefCounter($p_dependencies);
320 320
 
321
-                $this->setThiefCounter($counter+1, $p_dependencies); // Incremement the current state of the counter.
321
+                $this->setThiefCounter($counter + 1, $p_dependencies); // Incremement the current state of the counter.
322 322
 
323 323
                 if ($counter > 20 && rand(1, 3) == 3) {
324 324
                     // Only after many attacks do you have the chance to be attacked back by the group of thieves.
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 
342 342
             // Subtract the turn cost for attacking an npc
343 343
             // almost always 1 apart from perhaps oni or group-of-thieves
344
-            $player->changeTurns(-1*$turn_cost);
344
+            $player->changeTurns(-1 * $turn_cost);
345 345
 
346 346
             $player->save();
347 347
         }
@@ -351,7 +351,7 @@  discard block
 block discarded – undo
351 351
             'victim'       => $victim, // merge may override in theory
352 352
             'npc_template' => $npc_template,
353 353
             'attacked'     => 1,
354
-            'turns'        => $player? $player->turns : null,
354
+            'turns'        => $player ? $player->turns : null,
355 355
             'health'       => $health,
356 356
         ];
357 357
 
@@ -577,11 +577,11 @@  discard block
 block discarded – undo
577 577
      * Normal attack on a single thief.
578 578
      */
579 579
     private function attackNormalThief(Player $player) {
580
-        $damage = rand(0, 35);  // Damage done
580
+        $damage = rand(0, 35); // Damage done
581 581
         $gold   = 0;
582 582
 
583 583
         if ($victory = $player->harm($damage)) {
584
-            $gold = rand(0, 40);  // Gold in question
584
+            $gold = rand(0, 40); // Gold in question
585 585
 
586 586
             if ($damage > 30) { // Steal gold
587 587
                 $player->setGold(max(0, $player->gold - $gold));
Please login to merge, or discard this patch.
deploy/lib/data/Player.php 1 patch
Indentation   +611 added lines, -611 removed lines patch added patch discarded remove patch
@@ -45,176 +45,176 @@  discard block
 block discarded – undo
45 45
  * @property int status
46 46
  */
47 47
 class Player implements Character {
48
-    const HEALTH_PER_STAMINA = 2;
48
+	const HEALTH_PER_STAMINA = 2;
49 49
 	public $ip;
50 50
 	public $avatar_url;
51
-    private $data;
51
+	private $data;
52 52
 	private $vo;
53 53
 
54
-    /**
55
-     * Creates a new level 1 player object
56
-     */
57
-    public function __construct() {
58
-        $level = 1;
59
-
60
-        $this->vo                  = new PlayerVO();
61
-        $this->avatar_url          = null;
62
-        $this->uname               = null;
63
-        $this->health              = self::maxHealthByLevel($level);
64
-        $this->strength            = self::baseStrengthByLevel($level);
65
-        $this->speed               = self::baseSpeedByLevel($level);
66
-        $this->stamina             = self::baseStaminaByLevel($level);
67
-        $this->level               = $level;
68
-        $this->gold                = 100;
69
-        $this->turns               = 180;
70
-        $this->kills               = 0;
71
-        $this->status              = 0;
72
-        $this->member              = 0;
73
-        $this->days                = 0;
74
-        $this->bounty              = 0;
75
-        $this->energy              = 0;
76
-        $this->ki                  = 0;
77
-        $this->karma               = 0;
78
-        $this->avatar_type         = 1;
79
-        $this->messages            = '';
80
-        $this->description         = '';
81
-        $this->instincts           = '';
82
-        $this->traits              = '';
83
-        $this->beliefs             = '';
84
-        $this->goals               = '';
85
-        $this->last_started_attack = null;
86
-    }
87
-
88
-    /**
89
-     * @return string
90
-     */
91
-    public function __toString() {
92
-        return $this->name();
93
-    }
94
-
95
-    /**
96
-     * Magic method to provide accessors for properties
97
-     *
98
-     * @return mixed
99
-     */
100
-    public function __get($member_field) {
101
-        return $this->vo->$member_field;
102
-    }
103
-
104
-    /**
105
-     * Magic method to provide mutators for properties
106
-     *
107
-     * @return mixed
108
-     */
109
-    public function __set($member_field, $value) {
110
-        return $this->vo->$member_field = $value;
111
-    }
112
-
113
-    /**
114
-     * Magic method to handle isset() and empty() calls against properties
115
-     *
116
-     * @return boolean
117
-     */
118
-    public function __isset($member_field) {
119
-        return isset($this->vo->$member_field);
120
-    }
121
-
122
-    /**
123
-     *
124
-     */
125
-    public function __clone() {
126
-        $this->vo = clone $this->vo;
127
-    }
128
-
129
-    /**
130
-     * @return string
131
-     */
132
-    public function name() {
133
-        return $this->vo->uname;
134
-    }
135
-
136
-    /**
137
-     * @return int
138
-     */
54
+	/**
55
+	 * Creates a new level 1 player object
56
+	 */
57
+	public function __construct() {
58
+		$level = 1;
59
+
60
+		$this->vo                  = new PlayerVO();
61
+		$this->avatar_url          = null;
62
+		$this->uname               = null;
63
+		$this->health              = self::maxHealthByLevel($level);
64
+		$this->strength            = self::baseStrengthByLevel($level);
65
+		$this->speed               = self::baseSpeedByLevel($level);
66
+		$this->stamina             = self::baseStaminaByLevel($level);
67
+		$this->level               = $level;
68
+		$this->gold                = 100;
69
+		$this->turns               = 180;
70
+		$this->kills               = 0;
71
+		$this->status              = 0;
72
+		$this->member              = 0;
73
+		$this->days                = 0;
74
+		$this->bounty              = 0;
75
+		$this->energy              = 0;
76
+		$this->ki                  = 0;
77
+		$this->karma               = 0;
78
+		$this->avatar_type         = 1;
79
+		$this->messages            = '';
80
+		$this->description         = '';
81
+		$this->instincts           = '';
82
+		$this->traits              = '';
83
+		$this->beliefs             = '';
84
+		$this->goals               = '';
85
+		$this->last_started_attack = null;
86
+	}
87
+
88
+	/**
89
+	 * @return string
90
+	 */
91
+	public function __toString() {
92
+		return $this->name();
93
+	}
94
+
95
+	/**
96
+	 * Magic method to provide accessors for properties
97
+	 *
98
+	 * @return mixed
99
+	 */
100
+	public function __get($member_field) {
101
+		return $this->vo->$member_field;
102
+	}
103
+
104
+	/**
105
+	 * Magic method to provide mutators for properties
106
+	 *
107
+	 * @return mixed
108
+	 */
109
+	public function __set($member_field, $value) {
110
+		return $this->vo->$member_field = $value;
111
+	}
112
+
113
+	/**
114
+	 * Magic method to handle isset() and empty() calls against properties
115
+	 *
116
+	 * @return boolean
117
+	 */
118
+	public function __isset($member_field) {
119
+		return isset($this->vo->$member_field);
120
+	}
121
+
122
+	/**
123
+	 *
124
+	 */
125
+	public function __clone() {
126
+		$this->vo = clone $this->vo;
127
+	}
128
+
129
+	/**
130
+	 * @return string
131
+	 */
132
+	public function name() {
133
+		return $this->vo->uname;
134
+	}
135
+
136
+	/**
137
+	 * @return int
138
+	 */
139 139
 	public function id() {
140 140
 		return $this->vo->player_id;
141 141
 	}
142 142
 
143
-    /**
144
-     * Adds a defined numeric status constant to the binary string of statuses
145
-     */
146
-    public function addStatus($p_status) {
147
-        $status = self::validStatus($p_status);
143
+	/**
144
+	 * Adds a defined numeric status constant to the binary string of statuses
145
+	 */
146
+	public function addStatus($p_status) {
147
+		$status = self::validStatus($p_status);
148 148
 
149
-        if ($status > 0 && !$this->hasStatus($status)) {
150
-            if (gettype($this->status | $status) !== 'integer') {
151
-                throw new \InvalidArgumentException('invalid type for status');
152
-            }
149
+		if ($status > 0 && !$this->hasStatus($status)) {
150
+			if (gettype($this->status | $status) !== 'integer') {
151
+				throw new \InvalidArgumentException('invalid type for status');
152
+			}
153 153
 
154
-            $this->status = ($this->status | $status);
155
-        }
156
-    }
154
+			$this->status = ($this->status | $status);
155
+		}
156
+	}
157 157
 
158
-    /**
159
-     * Remove a numeric status from the binary string of status toggles.
160
-     */
161
-    public function subtractStatus($p_status) {
162
-        $status = self::validStatus($p_status);
158
+	/**
159
+	 * Remove a numeric status from the binary string of status toggles.
160
+	 */
161
+	public function subtractStatus($p_status) {
162
+		$status = self::validStatus($p_status);
163 163
 
164
-        if ($status > 0 && $this->hasStatus($status)) {
165
-            if (gettype($this->status & ~$status) !== 'integer') {
166
-                throw new \InvalidArgumentException('invalid type for status');
167
-            }
164
+		if ($status > 0 && $this->hasStatus($status)) {
165
+			if (gettype($this->status & ~$status) !== 'integer') {
166
+				throw new \InvalidArgumentException('invalid type for status');
167
+			}
168 168
 
169
-            $this->status = ($this->status & ~$status);
170
-        }
171
-    }
169
+			$this->status = ($this->status & ~$status);
170
+		}
171
+	}
172 172
 
173
-    /**
174
-     * Resets the binary status info to 0/none
175
-     */
173
+	/**
174
+	 * Resets the binary status info to 0/none
175
+	 */
176 176
 	public function resetStatus() {
177 177
 		$this->status = 0;
178 178
 	}
179 179
 
180
-    /**
181
-     * Determine whether a pc is effected by a certain status
182
-     * @return boolean
183
-     */
180
+	/**
181
+	 * Determine whether a pc is effected by a certain status
182
+	 * @return boolean
183
+	 */
184 184
 	public function hasStatus($p_status) {
185
-        $status = self::validStatus($p_status);
185
+		$status = self::validStatus($p_status);
186 186
 
187
-        return ((bool)$status && (bool)($this->status & $status));
187
+		return ((bool)$status && (bool)($this->status & $status));
188 188
 	}
189 189
 
190
-    /**
191
-     * Standard damage output from 1 to max
192
-     * @return int
193
-     */
190
+	/**
191
+	 * Standard damage output from 1 to max
192
+	 * @return int
193
+	 */
194 194
 	public function damage(Character $enemy=null){
195 195
 		return rand(1, $this->maxDamage($enemy));
196 196
 	}
197 197
 
198
-    /**
199
-     * Max damage capability of a character
200
-     *
201
-     * @return int
202
-     */
198
+	/**
199
+	 * Max damage capability of a character
200
+	 *
201
+	 * @return int
202
+	 */
203 203
 	public function maxDamage(Character $enemy=null){
204
-        return (int) ($this->getStrength() * 5 + $this->getSpeed());
205
-    }
204
+		return (int) ($this->getStrength() * 5 + $this->getSpeed());
205
+	}
206 206
 
207
-    /**
208
-     * @return int
209
-     */
207
+	/**
208
+	 * @return int
209
+	 */
210 210
 	public function getStrength() {
211
-        $str = NEW_PLAYER_INITIAL_STATS + (($this->level-1) * LEVEL_UP_STAT_RAISE);
212
-        if($this->hasStatus(STALKING)){
213
-            $str = (int) max(1, floor($str*1.4));
214
-        }
215
-        if ($this->hasStatus(STEALTH)) {
216
-            $str = (int) max(1, floor($str*0.7));
217
-        }
211
+		$str = NEW_PLAYER_INITIAL_STATS + (($this->level-1) * LEVEL_UP_STAT_RAISE);
212
+		if($this->hasStatus(STALKING)){
213
+			$str = (int) max(1, floor($str*1.4));
214
+		}
215
+		if ($this->hasStatus(STEALTH)) {
216
+			$str = (int) max(1, floor($str*0.7));
217
+		}
218 218
 		if ($this->hasStatus(WEAKENED)) {
219 219
 			return (int) max(1, $str-(ceil($str*.25))); // 75%
220 220
 		} elseif ($this->hasStatus(STR_UP2)) {
@@ -233,17 +233,17 @@  discard block
 block discarded – undo
233 233
 		$this->vo->strength = $str;
234 234
 	}
235 235
 
236
-    /**
237
-     * @return int
238
-     */
236
+	/**
237
+	 * @return int
238
+	 */
239 239
 	public function getSpeed() {
240
-        $speed = NEW_PLAYER_INITIAL_STATS + (($this->level -1) * LEVEL_UP_STAT_RAISE);
241
-        if($this->hasStatus(STALKING)){
242
-            $speed = (int) max(1, floor($speed*0.7));
243
-        }
244
-        if ($this->hasStatus(STEALTH)) {
245
-            $speed = (int) max(1, ceil($speed*1.3));
246
-        }
240
+		$speed = NEW_PLAYER_INITIAL_STATS + (($this->level -1) * LEVEL_UP_STAT_RAISE);
241
+		if($this->hasStatus(STALKING)){
242
+			$speed = (int) max(1, floor($speed*0.7));
243
+		}
244
+		if ($this->hasStatus(STEALTH)) {
245
+			$speed = (int) max(1, ceil($speed*1.3));
246
+		}
247 247
 		if ($this->hasStatus(SLOW)) {
248 248
 			return (int) ($speed-(ceil($speed*.25)));
249 249
 		} else {
@@ -258,17 +258,17 @@  discard block
 block discarded – undo
258 258
 		$this->vo->speed = $speed;
259 259
 	}
260 260
 
261
-    /**
262
-     * @return int
263
-     */
261
+	/**
262
+	 * @return int
263
+	 */
264 264
 	public function getStamina() {
265 265
 		$stam = NEW_PLAYER_INITIAL_STATS + (($this->level -1) * LEVEL_UP_STAT_RAISE);
266
-        if($this->hasStatus(STALKING)){
267
-            $stam = (int) max(1, floor($stam*0.9));
268
-        }
269
-        if ($this->hasStatus(STEALTH)) {
270
-            $stam = (int) max(1, ceil($stam*1.3));
271
-        }
266
+		if($this->hasStatus(STALKING)){
267
+			$stam = (int) max(1, floor($stam*0.9));
268
+		}
269
+		if ($this->hasStatus(STEALTH)) {
270
+			$stam = (int) max(1, ceil($stam*1.3));
271
+		}
272 272
 		if ($this->hasStatus(POISON)) {
273 273
 			return (int) ($stam-(ceil($stam*.25)));
274 274
 		} else {
@@ -283,9 +283,9 @@  discard block
 block discarded – undo
283 283
 		$this->vo->stamina = $stamina;
284 284
 	}
285 285
 
286
-    /**
287
-     * @return int
288
-     */
286
+	/**
287
+	 * @return int
288
+	 */
289 289
 	public function setKi($ki){
290 290
 		if($ki < 0){
291 291
 			throw new \InvalidArgumentException('Ki cannot be negative.');
@@ -293,9 +293,9 @@  discard block
 block discarded – undo
293 293
 		return $this->vo->ki = $ki;
294 294
 	}
295 295
 
296
-    /**
297
-     * @return int
298
-     */
296
+	/**
297
+	 * @return int
298
+	 */
299 299
 	public function setGold($gold) {
300 300
 		if ($gold < 0) {
301 301
 			throw new \InvalidArgumentException('Gold cannot be made negative.');
@@ -308,9 +308,9 @@  discard block
 block discarded – undo
308 308
 		return $this->vo->gold = $gold;
309 309
 	}
310 310
 
311
-    /**
312
-     * @return int
313
-     */
311
+	/**
312
+	 * @return int
313
+	 */
314 314
 	public function setBounty($bounty) {
315 315
 		if($bounty < 0){
316 316
 			throw new \InvalidArgumentException('Bounty cannot be made negative ['.(string)$bounty.'].');
@@ -323,18 +323,18 @@  discard block
 block discarded – undo
323 323
 
324 324
 	/**
325 325
 	 * Checks whether the character is still active.
326
-     *
327
-     * @return boolean
326
+	 *
327
+	 * @return boolean
328 328
 	 */
329 329
 	public function isActive() {
330 330
 		return (bool) $this->vo->active;
331 331
 	}
332 332
 
333
-    /**
334
-     * @return boolean
335
-     * hardcoded hack at the moment
336
-     * @note To be replaced by an in-database account toggle eventually
337
-     */
333
+	/**
334
+	 * @return boolean
335
+	 * hardcoded hack at the moment
336
+	 * @note To be replaced by an in-database account toggle eventually
337
+	 */
338 338
 	public function isAdmin() {
339 339
 		$name = strtolower($this->name());
340 340
 		if ($name == 'tchalvak' || $name == 'beagle' || $name == 'suavisimo') {
@@ -344,111 +344,111 @@  discard block
 block discarded – undo
344 344
 		return false;
345 345
 	}
346 346
 
347
-    /**
348
-     * Cleanup player to death state
349
-     *
350
-     * @return void
351
-     * @note
352
-     * This method writes the player object to the database
353
-     */
347
+	/**
348
+	 * Cleanup player to death state
349
+	 *
350
+	 * @return void
351
+	 * @note
352
+	 * This method writes the player object to the database
353
+	 */
354 354
 	public function death() {
355 355
 		$this->resetStatus();
356
-        $this->setHealth(0);
357
-        $this->save();
358
-	}
359
-
360
-    /**
361
-     * Changes the turns propety of the player object
362
-     *
363
-     * @param int $turns
364
-     * @return int The number of turns the player object now has
365
-     * @throws InvalidArgumentException $turns cannot be negative
366
-     */
367
-    public function setTurns($turns) {
368
-        if ($turns < 0) {
369
-            throw new \InvalidArgumentException('Turns cannot be made negative.');
370
-        }
371
-
372
-        return $this->vo->turns = $turns;
373
-    }
374
-
375
-    /**
376
-     * @deprecated
377
-     */
378
-    public function changeTurns($amount) {
379
-        return $this->setTurns($this->turns + (int) $amount);
380
-    }
381
-
382
-    /**
383
-     * @return integer
384
-     */
385
-    public function getMaxHealth() {
386
-        return NEW_PLAYER_INITIAL_HEALTH + ($this->getStamina()*static::HEALTH_PER_STAMINA);
387
-    }
388
-
389
-    /**
390
-     * Manipulates the data from the vo into the $this itself
391
-     *
392
-     * @return array
393
-     */
394
-    public function data() {
356
+		$this->setHealth(0);
357
+		$this->save();
358
+	}
359
+
360
+	/**
361
+	 * Changes the turns propety of the player object
362
+	 *
363
+	 * @param int $turns
364
+	 * @return int The number of turns the player object now has
365
+	 * @throws InvalidArgumentException $turns cannot be negative
366
+	 */
367
+	public function setTurns($turns) {
368
+		if ($turns < 0) {
369
+			throw new \InvalidArgumentException('Turns cannot be made negative.');
370
+		}
371
+
372
+		return $this->vo->turns = $turns;
373
+	}
374
+
375
+	/**
376
+	 * @deprecated
377
+	 */
378
+	public function changeTurns($amount) {
379
+		return $this->setTurns($this->turns + (int) $amount);
380
+	}
381
+
382
+	/**
383
+	 * @return integer
384
+	 */
385
+	public function getMaxHealth() {
386
+		return NEW_PLAYER_INITIAL_HEALTH + ($this->getStamina()*static::HEALTH_PER_STAMINA);
387
+	}
388
+
389
+	/**
390
+	 * Manipulates the data from the vo into the $this itself
391
+	 *
392
+	 * @return array
393
+	 */
394
+	public function data() {
395 395
 		if (!$this->data) {
396
-            $this->data = (array) $this->vo;
397
-            $this->data['next_level']    = $this->killsRequiredForNextLevel();
398
-            $this->data['max_health']    = $this->getMaxHealth();
399
-            $this->data['hp_percent']    = $this->health_percent();
400
-            $this->data['strength']      = $this->getStrength();
401
-            $this->data['speed']         = $this->getSpeed();
402
-            $this->data['stamina']       = $this->getStamina();
403
-            $this->data['max_turns']     = 100;
404
-            $this->data['turns_percent'] = min(100, round($this->data['turns']/$this->data['max_turns']*100));
405
-            $this->data['exp_percent']   = min(100, round(($this->data['kills']/$this->data['next_level'])*100));
406
-            $this->data['status_list']   = implode(', ', self::getStatusList($this->id()));
407
-            $this->data['hash']          = md5(implode($this->data));
408
-            $this->data['class_name']    = ucfirst($this->data['identity']); // A misnomer, identity is actually the class label
409
-            $this->data['clan_id']       = ($this->getClan() ? $this->getClan()->id : null);
410
-
411
-            unset($this->data['pname']);
412
-        }
413
-
414
-        return $this->data;
415
-    }
416
-
417
-    /**
418
-     * Return the data that should be publicly readable to javascript or the api while the player is logged in.
419
-     *
420
-     * @return array
421
-     */
422
-    public function publicData() {
423
-        $char_info = $this->data();
424
-        unset($char_info['ip'], $char_info['member'], $char_info['pname'], $char_info['verification_number'], $char_info['confirmed']);
425
-
426
-        return $char_info;
427
-    }
428
-
429
-    /**
430
-     * @return Clan
431
-     */
432
-    public function getClan() {
433
-        return Clan::findByMember($this);
434
-    }
396
+			$this->data = (array) $this->vo;
397
+			$this->data['next_level']    = $this->killsRequiredForNextLevel();
398
+			$this->data['max_health']    = $this->getMaxHealth();
399
+			$this->data['hp_percent']    = $this->health_percent();
400
+			$this->data['strength']      = $this->getStrength();
401
+			$this->data['speed']         = $this->getSpeed();
402
+			$this->data['stamina']       = $this->getStamina();
403
+			$this->data['max_turns']     = 100;
404
+			$this->data['turns_percent'] = min(100, round($this->data['turns']/$this->data['max_turns']*100));
405
+			$this->data['exp_percent']   = min(100, round(($this->data['kills']/$this->data['next_level'])*100));
406
+			$this->data['status_list']   = implode(', ', self::getStatusList($this->id()));
407
+			$this->data['hash']          = md5(implode($this->data));
408
+			$this->data['class_name']    = ucfirst($this->data['identity']); // A misnomer, identity is actually the class label
409
+			$this->data['clan_id']       = ($this->getClan() ? $this->getClan()->id : null);
410
+
411
+			unset($this->data['pname']);
412
+		}
413
+
414
+		return $this->data;
415
+	}
416
+
417
+	/**
418
+	 * Return the data that should be publicly readable to javascript or the api while the player is logged in.
419
+	 *
420
+	 * @return array
421
+	 */
422
+	public function publicData() {
423
+		$char_info = $this->data();
424
+		unset($char_info['ip'], $char_info['member'], $char_info['pname'], $char_info['verification_number'], $char_info['confirmed']);
425
+
426
+		return $char_info;
427
+	}
428
+
429
+	/**
430
+	 * @return Clan
431
+	 */
432
+	public function getClan() {
433
+		return Clan::findByMember($this);
434
+	}
435 435
 
436 436
 	/**
437 437
 	 * Heal the char with in the limits of their max
438
-     *
439
-     * @return int
438
+	 *
439
+	 * @return int
440 440
 	 */
441 441
 	public function heal($amount) {
442 442
 		// do not heal above max health
443
-        $heal = min($this->is_hurt_by(), $amount);
444
-        return $this->setHealth($this->health + $heal);
443
+		$heal = min($this->is_hurt_by(), $amount);
444
+		return $this->setHealth($this->health + $heal);
445 445
 	}
446 446
 
447 447
 	/**
448 448
 	 * Do some damage to the character
449
-     *
450
-     * @param int $damage
451
-     * @return int
449
+	 *
450
+	 * @param int $damage
451
+	 * @return int
452 452
 	 */
453 453
 	public function harm($damage) {
454 454
 		// Do not allow negative health
@@ -456,16 +456,16 @@  discard block
 block discarded – undo
456 456
 		return $this->setHealth($this->health - $actual_damage);
457 457
 	}
458 458
 
459
-    /**
460
-     * @return int
461
-     */
459
+	/**
460
+	 * @return int
461
+	 */
462 462
 	public function getHealth() {
463
-        return $this->health;
463
+		return $this->health;
464 464
 	}
465 465
 
466
-    /**
467
-     * @return int
468
-     */
466
+	/**
467
+	 * @return int
468
+	 */
469 469
 	public function setHealth($health) {
470 470
 		if ($health < 0) {
471 471
 			throw new \InvalidArgumentException('Health cannot be made negative.');
@@ -488,65 +488,65 @@  discard block
 block discarded – undo
488 488
 		);
489 489
 	}
490 490
 
491
-    /**
492
-     * Return the current percentage of the maximum health that a character could have.
493
-     * @return int
494
-     */
491
+	/**
492
+	 * Return the current percentage of the maximum health that a character could have.
493
+	 * @return int
494
+	 */
495 495
 	public function health_percent() {
496
-        return min(100, round(($this->health/$this->getMaxHealth())*100));
496
+		return min(100, round(($this->health/$this->getMaxHealth())*100));
497 497
 	}
498 498
 
499
-    /**
500
-     * @return int difficulty rating
501
-     */
499
+	/**
500
+	 * @return int difficulty rating
501
+	 */
502 502
 	public function difficulty(){
503 503
 		return (int) ( 10 + $this->getStrength() * 2 + $this->maxDamage());
504 504
 	}
505 505
 
506
-    /**
507
-     * @return int random private number unique to character
508
-     */
506
+	/**
507
+	 * @return int random private number unique to character
508
+	 */
509 509
 	public function getVerificationNumber(){
510 510
 		return $this->vo->verification_number;
511 511
 	}
512 512
 
513
-    /**
514
-     * @return string url for the gravatar of pc
515
-     */
516
-    public function avatarUrl() {
517
-        if (!isset($this->avatar_url) || $this->avatar_url === null) {
518
-            $this->avatar_url = $this->generateGravatarUrl();
519
-        }
520
-
521
-        return $this->avatar_url;
522
-    }
523
-
524
-    private function generateGravatarUrl() {
525
-        $account = Account::findByChar($this);
513
+	/**
514
+	 * @return string url for the gravatar of pc
515
+	 */
516
+	public function avatarUrl() {
517
+		if (!isset($this->avatar_url) || $this->avatar_url === null) {
518
+			$this->avatar_url = $this->generateGravatarUrl();
519
+		}
526 520
 
527
-        if (OFFLINE) {
528
-            return IMAGE_ROOT.'default_avatar.png';
529
-        } else if (!$this->vo || !$this->vo->avatar_type || !$account || !$account->email()) {
530
-            return '';
531
-        } else {
532
-            $email       = $account->email();
521
+		return $this->avatar_url;
522
+	}
533 523
 
534
-            $def         = 'monsterid'; // Default image or image class.
535
-            // other options: wavatar (polygonal creature) , monsterid, identicon (random shape)
536
-            $base        = "http://www.gravatar.com/avatar/";
537
-            $hash        = md5(trim(strtolower($email)));
538
-            $no_gravatar = "d=".urlencode($def);
539
-            $size        = 80;
540
-            $rating      = "r=x";
541
-            $res         = $base.$hash."?".implode('&', [$no_gravatar, $size, $rating]);
524
+	private function generateGravatarUrl() {
525
+		$account = Account::findByChar($this);
542 526
 
543
-            return $res;
544
-        }
527
+		if (OFFLINE) {
528
+			return IMAGE_ROOT.'default_avatar.png';
529
+		} else if (!$this->vo || !$this->vo->avatar_type || !$account || !$account->email()) {
530
+			return '';
531
+		} else {
532
+			$email       = $account->email();
533
+
534
+			$def         = 'monsterid'; // Default image or image class.
535
+			// other options: wavatar (polygonal creature) , monsterid, identicon (random shape)
536
+			$base        = "http://www.gravatar.com/avatar/";
537
+			$hash        = md5(trim(strtolower($email)));
538
+			$no_gravatar = "d=".urlencode($def);
539
+			$size        = 80;
540
+			$rating      = "r=x";
541
+			$res         = $base.$hash."?".implode('&', [$no_gravatar, $size, $rating]);
542
+
543
+			return $res;
544
+		}
545 545
 	}
546 546
 
547 547
 	/**
548 548
 	 * Persist object to database
549
-     *
549
+	 *
550 550
 	 * @return Player
551 551
 	 */
552 552
 	public function save() {
@@ -556,175 +556,175 @@  discard block
 block discarded – undo
556 556
 		return $this;
557 557
 	}
558 558
 
559
-     /**
560
-     * Check whether the player is the leader of their clan.
561
-     * @return boolean
562
-     */
563
-    public function isClanLeader() {
564
-        return (($clan = Clan::findByMember($this)) && $this->id() == $clan->getLeaderID());
565
-    }
566
-
567
-
568
-    /**
569
-     * Get the information for a single class' data, generally the characters
570
-     * @param string $class_identity
571
-     * @return array of class data
572
-     */
573
-    private function obtainSingleClassData($class_identity){
574
-            return query_row(
575
-                'select class_id, identity, class_name, theme, class_note, class_tier, class_desc, class_icon from class where class.identity = :class',
576
-                [':class' => $class_identity]
577
-            );
578
-    }
579
-
580
-    /**
581
-     * Set the character's class, using the identity.
582
-     * @return string|null error string if fails
583
-     */
584
-    public function setClass($new_class) {
585
-        $class_data = $this->obtainSingleClassData(strtolower($new_class));
586
-        if($class_data === false || $class_data === null){
587
-            return "That class was not an option to change into.";
588
-        } else {
589
-            // Update the only place in the database where a players class is determined
590
-            $up = "UPDATE players SET _class_id = :class_id WHERE player_id = :char_id";
591
-            query($up, [
592
-                ':class_id' => $class_data['class_id'],
593
-                ':char_id'  => $this->id(),
594
-            ]);
595
-
596
-            $this->class_name    = $class_data['class_name'];
597
-            $this->theme         = $class_data['theme'];
598
-            $this->vo->identity  = $class_data['identity'];
599
-            $this->vo->_class_id = $class_data['class_id'];
600
-
601
-            return null;
602
-        }
603
-    }
604
-
605
-    /**
606
-     * Get the ninja's class's name.
607
-     * @return string
608
-     */
609
-    public function getClassName() {
610
-        return $this->vo->class_name;
611
-    }
612
-
613
-    /**
614
-     * The number of kills needed to level up to the next level.
615
-     *
616
-     * 5 more kills in cost for every level you go up.
617
-     * @return int
618
-     */
619
-    public function killsRequiredForNextLevel() {
620
-       return $this->level*5;
621
-    }
622
-
623
-    /**
624
-     * Takes in a Character and adds kills to that character.
625
-     * @return int
626
-     */
627
-    public function addKills($amount) {
628
-        return $this->changeKills((int)abs($amount));
629
-    }
630
-
631
-    /**
632
-     * Takes in a Character and removes kills from that character.
633
-     * @return int
634
-     */
635
-    public function subtractKills($amount) {
636
-        return $this->changeKills(-1*((int)abs($amount)));
637
-    }
638
-
639
-    /**
640
-     * Change the kills amount of a char, and levels them up when necessary.
641
-     * @return int
642
-     */
643
-    private function changeKills($amount) {
644
-        $amount = (int)$amount;
645
-
646
-        GameLog::updateLevellingLog($this->id(), $amount);
647
-
648
-        if ($amount !== 0) { // Ignore changes that amount to zero.
649
-            if ($amount > 0) { // when adding kills, check if levelling occurs
650
-                $this->levelUp();
651
-            }
652
-
653
-            query(
654
-                "UPDATE players SET kills = kills + CASE WHEN kills + :amount1 < 0 THEN kills*(-1) ELSE :amount2 END WHERE player_id = :player_id",
655
-                [
656
-                    ':amount1'   => [$amount, PDO::PARAM_INT],
657
-                    ':amount2'   => [$amount, PDO::PARAM_INT],
658
-                    ':player_id' => $this->id(),
659
-                ]
660
-            );
661
-        }
662
-
663
-        return $this->vo->kills = query_item(
664
-            "SELECT kills FROM players WHERE player_id = :player_id",
665
-            [
666
-                ':player_id' => [$this->id(), PDO::PARAM_INT],
667
-            ]
668
-        );
669
-    }
670
-
671
-    /**
672
-     * Leveling up Function
673
-     *
674
-     * @return boolean
675
-     */
676
-    public function levelUp() {
677
-        $health_to_add     = 100;
678
-        $turns_to_give     = 50;
679
-        $ki_to_give        = 50;
680
-        $stat_value_to_add = 5;
681
-        $karma_to_give     = 1;
682
-
683
-        if ($this->isAdmin()) { // If the character is an admin, do not auto-level
684
-            return false;
685
-        } else { // For normal characters, do auto-level
686
-            // Have to be under the max level and have enough kills.
687
-            $level_up_possible = (
688
-                ($this->level + 1 <= MAX_PLAYER_LEVEL) &&
689
-                ($this->kills >= $this->killsRequiredForNextLevel())
690
-            );
691
-
692
-            if ($level_up_possible) { // Perform the level up actions
693
-                $this->setHealth($this->health + $health_to_add);
694
-                $this->setTurns($this->turns   + $turns_to_give);
695
-                $this->setKi($this->ki         + $ki_to_give);
696
-
697
-                // Must read from VO for these as accessors return modified values
698
-                $this->setStamina($this->vo->stamina   + $stat_value_to_add);
699
-                $this->setStrength($this->vo->strength + $stat_value_to_add);
700
-                $this->setSpeed($this->vo->speed       + $stat_value_to_add);
701
-
702
-                // no mutator for these yet
703
-                $this->vo->kills = max(0, $this->kills - $this->killsRequiredForNextLevel());
704
-                $this->vo->karma = ($this->karma + $karma_to_give);
705
-                $this->vo->level = ($this->level + 1);
706
-
707
-                $this->save();
708
-
709
-                GameLog::recordLevelUp($this->id());
710
-
711
-                $account = Account::findByChar($this);
712
-                $account->setKarmaTotal($account->getKarmaTotal() + $karma_to_give);
713
-                $account->save();
714
-
715
-                // Send a level-up message, for those times when auto-levelling happens.
716
-                Event::create($this->id(), $this->id(),
717
-                    "You levelled up! Your strength raised by $stat_value_to_add, speed by $stat_value_to_add, stamina by $stat_value_to_add, Karma by $karma_to_give, and your Ki raised $ki_to_give! You gained some health and turns, as well! You are now a level {$this->level} ninja! Go kill some stuff.");
718
-                return true;
719
-            } else {
720
-                return false;
721
-            }
722
-        }
723
-    }
559
+	 /**
560
+	  * Check whether the player is the leader of their clan.
561
+	  * @return boolean
562
+	  */
563
+	public function isClanLeader() {
564
+		return (($clan = Clan::findByMember($this)) && $this->id() == $clan->getLeaderID());
565
+	}
566
+
567
+
568
+	/**
569
+	 * Get the information for a single class' data, generally the characters
570
+	 * @param string $class_identity
571
+	 * @return array of class data
572
+	 */
573
+	private function obtainSingleClassData($class_identity){
574
+			return query_row(
575
+				'select class_id, identity, class_name, theme, class_note, class_tier, class_desc, class_icon from class where class.identity = :class',
576
+				[':class' => $class_identity]
577
+			);
578
+	}
579
+
580
+	/**
581
+	 * Set the character's class, using the identity.
582
+	 * @return string|null error string if fails
583
+	 */
584
+	public function setClass($new_class) {
585
+		$class_data = $this->obtainSingleClassData(strtolower($new_class));
586
+		if($class_data === false || $class_data === null){
587
+			return "That class was not an option to change into.";
588
+		} else {
589
+			// Update the only place in the database where a players class is determined
590
+			$up = "UPDATE players SET _class_id = :class_id WHERE player_id = :char_id";
591
+			query($up, [
592
+				':class_id' => $class_data['class_id'],
593
+				':char_id'  => $this->id(),
594
+			]);
595
+
596
+			$this->class_name    = $class_data['class_name'];
597
+			$this->theme         = $class_data['theme'];
598
+			$this->vo->identity  = $class_data['identity'];
599
+			$this->vo->_class_id = $class_data['class_id'];
600
+
601
+			return null;
602
+		}
603
+	}
604
+
605
+	/**
606
+	 * Get the ninja's class's name.
607
+	 * @return string
608
+	 */
609
+	public function getClassName() {
610
+		return $this->vo->class_name;
611
+	}
612
+
613
+	/**
614
+	 * The number of kills needed to level up to the next level.
615
+	 *
616
+	 * 5 more kills in cost for every level you go up.
617
+	 * @return int
618
+	 */
619
+	public function killsRequiredForNextLevel() {
620
+	   return $this->level*5;
621
+	}
622
+
623
+	/**
624
+	 * Takes in a Character and adds kills to that character.
625
+	 * @return int
626
+	 */
627
+	public function addKills($amount) {
628
+		return $this->changeKills((int)abs($amount));
629
+	}
630
+
631
+	/**
632
+	 * Takes in a Character and removes kills from that character.
633
+	 * @return int
634
+	 */
635
+	public function subtractKills($amount) {
636
+		return $this->changeKills(-1*((int)abs($amount)));
637
+	}
638
+
639
+	/**
640
+	 * Change the kills amount of a char, and levels them up when necessary.
641
+	 * @return int
642
+	 */
643
+	private function changeKills($amount) {
644
+		$amount = (int)$amount;
645
+
646
+		GameLog::updateLevellingLog($this->id(), $amount);
647
+
648
+		if ($amount !== 0) { // Ignore changes that amount to zero.
649
+			if ($amount > 0) { // when adding kills, check if levelling occurs
650
+				$this->levelUp();
651
+			}
652
+
653
+			query(
654
+				"UPDATE players SET kills = kills + CASE WHEN kills + :amount1 < 0 THEN kills*(-1) ELSE :amount2 END WHERE player_id = :player_id",
655
+				[
656
+					':amount1'   => [$amount, PDO::PARAM_INT],
657
+					':amount2'   => [$amount, PDO::PARAM_INT],
658
+					':player_id' => $this->id(),
659
+				]
660
+			);
661
+		}
662
+
663
+		return $this->vo->kills = query_item(
664
+			"SELECT kills FROM players WHERE player_id = :player_id",
665
+			[
666
+				':player_id' => [$this->id(), PDO::PARAM_INT],
667
+			]
668
+		);
669
+	}
670
+
671
+	/**
672
+	 * Leveling up Function
673
+	 *
674
+	 * @return boolean
675
+	 */
676
+	public function levelUp() {
677
+		$health_to_add     = 100;
678
+		$turns_to_give     = 50;
679
+		$ki_to_give        = 50;
680
+		$stat_value_to_add = 5;
681
+		$karma_to_give     = 1;
682
+
683
+		if ($this->isAdmin()) { // If the character is an admin, do not auto-level
684
+			return false;
685
+		} else { // For normal characters, do auto-level
686
+			// Have to be under the max level and have enough kills.
687
+			$level_up_possible = (
688
+				($this->level + 1 <= MAX_PLAYER_LEVEL) &&
689
+				($this->kills >= $this->killsRequiredForNextLevel())
690
+			);
691
+
692
+			if ($level_up_possible) { // Perform the level up actions
693
+				$this->setHealth($this->health + $health_to_add);
694
+				$this->setTurns($this->turns   + $turns_to_give);
695
+				$this->setKi($this->ki         + $ki_to_give);
696
+
697
+				// Must read from VO for these as accessors return modified values
698
+				$this->setStamina($this->vo->stamina   + $stat_value_to_add);
699
+				$this->setStrength($this->vo->strength + $stat_value_to_add);
700
+				$this->setSpeed($this->vo->speed       + $stat_value_to_add);
701
+
702
+				// no mutator for these yet
703
+				$this->vo->kills = max(0, $this->kills - $this->killsRequiredForNextLevel());
704
+				$this->vo->karma = ($this->karma + $karma_to_give);
705
+				$this->vo->level = ($this->level + 1);
706
+
707
+				$this->save();
708
+
709
+				GameLog::recordLevelUp($this->id());
710
+
711
+				$account = Account::findByChar($this);
712
+				$account->setKarmaTotal($account->getKarmaTotal() + $karma_to_give);
713
+				$account->save();
714
+
715
+				// Send a level-up message, for those times when auto-levelling happens.
716
+				Event::create($this->id(), $this->id(),
717
+					"You levelled up! Your strength raised by $stat_value_to_add, speed by $stat_value_to_add, stamina by $stat_value_to_add, Karma by $karma_to_give, and your Ki raised $ki_to_give! You gained some health and turns, as well! You are now a level {$this->level} ninja! Go kill some stuff.");
718
+				return true;
719
+			} else {
720
+				return false;
721
+			}
722
+		}
723
+	}
724 724
 
725 725
 	/**
726 726
 	 * Find a player by primary key
727
-     * @param int|null $id
727
+	 * @param int|null $id
728 728
 	 * @return Player|null
729 729
 	 */
730 730
 	public static function find($id){
@@ -742,138 +742,138 @@  discard block
 block discarded – undo
742 742
 		return $player;
743 743
 	}
744 744
 
745
-    /**
746
-     * Find a char by playable for account
747
-     * @param int|null $account_id
748
-     * @return Player|null
749
-     */
750
-    public static function findPlayable($account_id){
751
-        // Two db calls for now
752
-        $pid = query_item('select player_id from players p 
745
+	/**
746
+	 * Find a char by playable for account
747
+	 * @param int|null $account_id
748
+	 * @return Player|null
749
+	 */
750
+	public static function findPlayable($account_id){
751
+		// Two db calls for now
752
+		$pid = query_item('select player_id from players p 
753 753
             join account_players ap on p.player_id = ap._player_id
754 754
             join accounts a on a.account_id = ap._account_id
755 755
             where account_id = :aid
756 756
             order by p.created_date asc, a.last_login desc
757 757
             limit 1', [':aid'=>[$account_id, PDO::PARAM_INT]]);
758
-        return self::find($pid);
759
-    }
760
-
761
-    /**
762
-     * Find player by name
763
-     * @return Player|null
764
-     */
765
-    public static function findByName($name){
766
-        $id = query_item('select player_id from players where lower(uname) = lower(:name) limit 1', [':name'=>$name]);
767
-        return self::find($id);
768
-    }
769
-
770
-    /**
771
-     * query the recently active players
772
-     * @return array Array of data not of player objects
773
-     */
774
-    public static function findActive($limit=5, $alive_only=true) {
775
-        $where_cond = ($alive_only ? ' AND health > 0' : '');
776
-        $sel = "SELECT uname, player_id FROM players WHERE active = 1 $where_cond ORDER BY last_started_attack DESC LIMIT :limit";
777
-        $active_ninjas = query_array($sel, array(':limit'=>array($limit, PDO::PARAM_INT)));
778
-        return $active_ninjas;
779
-    }
780
-
781
-    /**
782
-     * @return integer|null
783
-     * @note this needs review overall, as nonexistent high int statuses will false positive
784
-     */
785
-    public static function validStatus($dirty) {
786
-        if (is_numeric($dirty) && (int)$dirty == $dirty) {
787
-            return (int) $dirty;
788
-        } elseif (is_string($dirty)) {
789
-            $status = strtoupper($dirty);
790
-
791
-            if (defined($status)) {
792
-                return (int) constant($status);
793
-            } else {
794
-                return null;
795
-            }
796
-        } else {
797
-            return null;
798
-        }
799
-    }
800
-
801
-    /**
802
-     * Get the different statuses a character is affected by.
803
-     *
804
-     * @param int|null $target the target id, username if self targetting.
805
-     * @return string[]
806
-     *
807
-     */
808
-    public static function getStatusList($target=null) {
809
-        $states = array();
810
-        $target = (isset($target) && (int)$target == $target ? $target : SessionFactory::getSession()->get('player_id'));
811
-
812
-        // Default to showing own status.
813
-        $target = self::find($target);
814
-
815
-        if (!$target || $target->health < 1) {
816
-            $states[] = 'Dead';
817
-        } else { // *** Other statuses only display if not dead.
818
-            if ($target->health < 80) {
819
-                $states[] = 'Injured';
820
-            } else {
821
-                $states[] = 'Healthy';
822
-            }
823
-
824
-            // The visibly viewable statuses.
825
-            if ($target->hasStatus(STEALTH)) { $states[] = 'Stealthed'; }
826
-            if ($target->hasStatus(POISON)) { $states[] = 'Poisoned'; }
827
-            if ($target->hasStatus(WEAKENED)) { $states[] = 'Weakened'; }
828
-            if ($target->hasStatus(FROZEN)) { $states[] = 'Frozen'; }
829
-            if ($target->hasStatus(STR_UP1)) { $states[] = 'Buff'; }
830
-            if ($target->hasStatus(STR_UP2)) { $states[] = 'Strength+'; }
831
-
832
-            // If any of the shield skills are up, show a single status state for any.
833
-            if ($target->hasStatus(FIRE_RESISTING) || $target->hasStatus(INSULATED) || $target->hasStatus(GROUNDED)
834
-                || $target->hasStatus(BLESSED) || $target->hasStatus(IMMUNIZED)
835
-                || $target->hasStatus(ACID_RESISTING)) {
836
-                $states[] = 'Shielded';
837
-            }
838
-        }
839
-
840
-        return $states;
841
-    }
842
-
843
-    /**
844
-     * Calculate a max health by a level, this is actually only the base maximum
845
-     * since changes in stamina can change the current player's maximum
846
-     *
847
-     * @return integer The health points
848
-     */
849
-    public static function maxHealthByLevel($level) {
850
-        return (int) NEW_PLAYER_INITIAL_HEALTH + (int) (self::baseStaminaByLevel($level) * self::HEALTH_PER_STAMINA);
851
-    }
852
-
853
-    /**
854
-     * Calculate a base str by level
855
-     *
856
-     * @return integer strength
857
-     */
858
-    public static function baseStrengthByLevel($level) {
859
-        return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
860
-    }
861
-
862
-    /**
863
-     * Calculate a base speed by level
864
-     *
865
-     * @return integer speed
866
-     */
867
-    public static function baseSpeedByLevel($level) {
868
-        return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
869
-    }
870
-
871
-    /**
872
-     * Calculate a base stamina by level
873
-     *
874
-     * @return integer speed
875
-     */
876
-    public static function baseStaminaByLevel($level) {
877
-        return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
878
-    }
758
+		return self::find($pid);
759
+	}
760
+
761
+	/**
762
+	 * Find player by name
763
+	 * @return Player|null
764
+	 */
765
+	public static function findByName($name){
766
+		$id = query_item('select player_id from players where lower(uname) = lower(:name) limit 1', [':name'=>$name]);
767
+		return self::find($id);
768
+	}
769
+
770
+	/**
771
+	 * query the recently active players
772
+	 * @return array Array of data not of player objects
773
+	 */
774
+	public static function findActive($limit=5, $alive_only=true) {
775
+		$where_cond = ($alive_only ? ' AND health > 0' : '');
776
+		$sel = "SELECT uname, player_id FROM players WHERE active = 1 $where_cond ORDER BY last_started_attack DESC LIMIT :limit";
777
+		$active_ninjas = query_array($sel, array(':limit'=>array($limit, PDO::PARAM_INT)));
778
+		return $active_ninjas;
779
+	}
780
+
781
+	/**
782
+	 * @return integer|null
783
+	 * @note this needs review overall, as nonexistent high int statuses will false positive
784
+	 */
785
+	public static function validStatus($dirty) {
786
+		if (is_numeric($dirty) && (int)$dirty == $dirty) {
787
+			return (int) $dirty;
788
+		} elseif (is_string($dirty)) {
789
+			$status = strtoupper($dirty);
790
+
791
+			if (defined($status)) {
792
+				return (int) constant($status);
793
+			} else {
794
+				return null;
795
+			}
796
+		} else {
797
+			return null;
798
+		}
799
+	}
800
+
801
+	/**
802
+	 * Get the different statuses a character is affected by.
803
+	 *
804
+	 * @param int|null $target the target id, username if self targetting.
805
+	 * @return string[]
806
+	 *
807
+	 */
808
+	public static function getStatusList($target=null) {
809
+		$states = array();
810
+		$target = (isset($target) && (int)$target == $target ? $target : SessionFactory::getSession()->get('player_id'));
811
+
812
+		// Default to showing own status.
813
+		$target = self::find($target);
814
+
815
+		if (!$target || $target->health < 1) {
816
+			$states[] = 'Dead';
817
+		} else { // *** Other statuses only display if not dead.
818
+			if ($target->health < 80) {
819
+				$states[] = 'Injured';
820
+			} else {
821
+				$states[] = 'Healthy';
822
+			}
823
+
824
+			// The visibly viewable statuses.
825
+			if ($target->hasStatus(STEALTH)) { $states[] = 'Stealthed'; }
826
+			if ($target->hasStatus(POISON)) { $states[] = 'Poisoned'; }
827
+			if ($target->hasStatus(WEAKENED)) { $states[] = 'Weakened'; }
828
+			if ($target->hasStatus(FROZEN)) { $states[] = 'Frozen'; }
829
+			if ($target->hasStatus(STR_UP1)) { $states[] = 'Buff'; }
830
+			if ($target->hasStatus(STR_UP2)) { $states[] = 'Strength+'; }
831
+
832
+			// If any of the shield skills are up, show a single status state for any.
833
+			if ($target->hasStatus(FIRE_RESISTING) || $target->hasStatus(INSULATED) || $target->hasStatus(GROUNDED)
834
+				|| $target->hasStatus(BLESSED) || $target->hasStatus(IMMUNIZED)
835
+				|| $target->hasStatus(ACID_RESISTING)) {
836
+				$states[] = 'Shielded';
837
+			}
838
+		}
839
+
840
+		return $states;
841
+	}
842
+
843
+	/**
844
+	 * Calculate a max health by a level, this is actually only the base maximum
845
+	 * since changes in stamina can change the current player's maximum
846
+	 *
847
+	 * @return integer The health points
848
+	 */
849
+	public static function maxHealthByLevel($level) {
850
+		return (int) NEW_PLAYER_INITIAL_HEALTH + (int) (self::baseStaminaByLevel($level) * self::HEALTH_PER_STAMINA);
851
+	}
852
+
853
+	/**
854
+	 * Calculate a base str by level
855
+	 *
856
+	 * @return integer strength
857
+	 */
858
+	public static function baseStrengthByLevel($level) {
859
+		return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
860
+	}
861
+
862
+	/**
863
+	 * Calculate a base speed by level
864
+	 *
865
+	 * @return integer speed
866
+	 */
867
+	public static function baseSpeedByLevel($level) {
868
+		return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
869
+	}
870
+
871
+	/**
872
+	 * Calculate a base stamina by level
873
+	 *
874
+	 * @return integer speed
875
+	 */
876
+	public static function baseStaminaByLevel($level) {
877
+		return (int) NEW_PLAYER_INITIAL_STATS + (LEVEL_UP_STAT_RAISE * ($level-1));
878
+	}
879 879
 }
Please login to merge, or discard this patch.