Issues (1369)

classes/Fleet/MissionExploreResult.php (7 issues)

1
<?php /** @noinspection PhpDeprecationInspection */
2
3
/** Created by Gorlum 09.05.2025 18:56 */
4
5
namespace Fleet;
6
7
use SN;
8
9
class MissionExploreResult {
10
  /** @var string Key in outcome config for roll value */
11
  const K_ROLL_VALUE = 'value';
12
13
  /** @var int Max DM can be found in 1 expedition */
14
  const MAX_DM = CONST_10K;
15
16
  /** @var int $valueRolled mt_rand() value rolled [0,max_chance] that determine current expedition outcome */
17
  public $valueRolled = Constants::OUTCOME_NOT_CALCULATED;
18
  /** @var int $outcome Expedition outcome */
19
  public $outcome = Constants::OUTCOME_NOT_CALCULATED;
20
  /**@var array $currentOutcomeConfig Current outcome config */
21
  public $currentOutcomeConfig = [];
22
23
  // Outcomes with variants - sub-outcomes
24
  /** @var float $subOutcomeProbability Normalized probability [0,1] of sub-outcome */
25
  public $subOutcomeProbability = Constants::OUTCOME_NOT_CALCULATED;
26
  /** @var int $subOutcome Secondary outcome (sub-outcome) for variable outcomes */
27
  public $subOutcome = Constants::OUTCOME_NOT_CALCULATED;
28
  /** @var float $gainShare Share of total resources to gain - depends on sub-outcome */
29
  public $gainShare = 0;
30
31
  // Units/Resources changes
32
  /** @var int[] $shipsFound Ships found during current expedition */
33
  public $shipsFound = [];
34
  /** @var int[] $shipsLost Ships lost during current expedition */
35
  public $shipsLost = [];
36
  /** @var float[] $resourcesFound Resource amounts found during current expedition */
37
  public $resourcesFound = [];
38
  /** @var int $darkMatterFound Dark Matter found during current expedition */
39
  public $darkMatterFound = 0;
40
41
  // Other variables
42
  /** @var array $ships Current list of ships within mission. CAN be changed by outcomes and SHOULD be changed if ships lost/found */
43
  public $ships = [];
44
  /** @var int $fleetCapacityFree Free fleet capacity left */
45
  public $fleetCapacityFree = 0;
46
  /** @var int $shipsCostInMetal Total ships cost in metal */
47
  public $shipsCostInMetal = 0;
48
49
  /** @var ?FleetDispatchEvent $fleetEvent Event currently processed */
50
  public $fleetEvent = null;
51
52
  /** @var float $timeStart Timestamp with ms when started expedition processing */
53
  protected $timeStart = 0;
54
55
  /** @var array[] $configs */
56
  public static $configs = [
57
    Constants::OUTCOME_NONE                       => [
58
      Constants::K_OUTCOME      => Constants::OUTCOME_NONE,
59
      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_NEUTRAL,
60
      P_CHANCE                  => Constants::OUTCOME_EXPEDITION_NOTHING_DEFAULT_CHANCE,
61
    ],
62
    Constants::EXPEDITION_OUTCOME_LOST_FLEET      => [
63
      Constants::K_OUTCOME      => Constants::EXPEDITION_OUTCOME_LOST_FLEET,
64
      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_BAD,
65
      P_CHANCE                  => 9,
66
    ],
67
    Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL  => [
68
      Constants::K_OUTCOME      => Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL,
69
      Constants::K_OUTCOME_TYPE => Constants::OUTCOME_TYPE_BAD,
70
      P_CHANCE                  => 3,
71
    ],
72
    Constants::EXPEDITION_OUTCOME_FOUND_FLEET     => [
73
      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_FLEET,
74
      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,
75
      P_CHANCE                       => 200,
76
      'percent'                      => [0 => 0.1, 1 => 0.02, 2 => 0.01,],
77
      Constants::K_OUTCOME_SECONDARY => [
78
        [P_CHANCE => 90, P_MULTIPLIER => 0.01, P_MESSAGE_ID => 2,],
79
        [P_CHANCE => 9, P_MULTIPLIER => 0.02, P_MESSAGE_ID => 1,],
80
        [P_CHANCE => 1, P_MULTIPLIER => 0.10, P_MESSAGE_ID => 0,],
81
      ],
82
    ],
83
    Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES => [
84
      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES,
85
      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,
86
      P_CHANCE                       => 300,
87
      'percent'                      => [0 => 0.1, 1 => 0.050, 2 => 0.025,],
88
      Constants::K_OUTCOME_SECONDARY => [
89
        [P_CHANCE => 90, P_MULTIPLIER => 0.025, P_MESSAGE_ID => 2,],
90
        [P_CHANCE => 9, P_MULTIPLIER => 0.050, P_MESSAGE_ID => 1,],
91
        [P_CHANCE => 1, P_MULTIPLIER => 0.100, P_MESSAGE_ID => 0,],
92
      ],
93
    ],
94
    Constants::EXPEDITION_OUTCOME_FOUND_DM        => [
95
      Constants::K_OUTCOME           => Constants::EXPEDITION_OUTCOME_FOUND_DM,
96
      Constants::K_OUTCOME_TYPE      => Constants::OUTCOME_TYPE_GOOD,
97
      P_CHANCE                       => 100,
98
      'percent'                      => [0 => 0.0100, 1 => 0.0040, 2 => 0.0010,],
99
      Constants::K_OUTCOME_SECONDARY => [
100
        [P_CHANCE => 90, P_MULTIPLIER => 0.0010, /*P_MESSAGE_ID => 2,*/],
101
        [P_CHANCE => 9, P_MULTIPLIER => 0.0040, /*P_MESSAGE_ID => 1,*/],
102
        [P_CHANCE => 1, P_MULTIPLIER => 0.0100, /*P_MESSAGE_ID => 0,*/],
103
      ],
104
    ],
105
    /*
106
    FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT => array(
107
      'outcome' => FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT,
108
      P_CHANCE => 10,
109
    ),
110
    */
111
  ];
112
113
  /** @var array[] $shipData */
114
  public static $shipData = [];
115
  /** @var float[] $rates Resources exchange rates */
116
  public static $rates = [];
117
118
  public function __construct() {
119
    self::getShipData();
120
    self::getExchangeRates();
121
  }
122
123
  /**
124
   * @return int
125
   */
126
  public function flt_mission_explore(FleetDispatchEvent $fleetEvent) {
127
    if ($fleetEvent->event != EVENT_FLT_ACCOMPLISH) {
128
      return CACHE_NONE;
129
    }
130
131
    // Preparing for expedition
132
    $this->timeStart = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $timeStart is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
133
134
    $this->resetExpedition($fleetEvent);
135
136
    $this->calcSecondaryData();
137
138
    // Calculating mission outcome
139
140
    // Making a copy of outcome configs to tamper with
141
    $outcomeConfigs = static::$configs;
142
143
    $outcomeConfigs = $this->adjustNoneChance($outcomeConfigs);
144
145
    list($outcomeConfigs, $chance_max) = $this->calculateRollValues($outcomeConfigs);
146
147
148
    // Rolling value which wil determine outcome
149
    $this->valueRolled = mt_rand(0, ceil($chance_max));
0 ignored issues
show
ceil($chance_max) of type double is incompatible with the type integer expected by parameter $max of mt_rand(). ( Ignorable by Annotation )

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

149
    $this->valueRolled = mt_rand(0, /** @scrutinizer ignore-type */ ceil($chance_max));
Loading history...
$chance_max of type array is incompatible with the type double|integer expected by parameter $num of ceil(). ( Ignorable by Annotation )

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

149
    $this->valueRolled = mt_rand(0, ceil(/** @scrutinizer ignore-type */ $chance_max));
Loading history...
150
    // NOTHING => 200, LOST_FLEET => 209, LOST_FLEET_ALL => 212, FOUND_FLEET => 412, RESOURCES => 712, FOUND_DM => 812
151
    // $this->valueRolled = 409; // DEBUG  comment!
152
    // Determining outcome
153
    foreach ($outcomeConfigs as $key1 => $config) {
0 ignored issues
show
The expression $outcomeConfigs of type integer is not traversable.
Loading history...
154
      if (!$config[P_CHANCE]) {
155
        continue;
156
      }
157
      $this->outcome        = $key1;
158
      if ($this->valueRolled <= $config [self::K_ROLL_VALUE]) {
159
        break;
160
      }
161
    }
162
    // Fallback in case something went wrong
163
    if ($this->outcome == Constants::OUTCOME_NOT_CALCULATED) {
164
      $this->outcome = Constants::OUTCOME_NONE;
165
    }
166
    $this->currentOutcomeConfig = $outcomeConfigs[$this->outcome];
167
168
    // Вычисляем вероятность выпадения данного числа в общем пуле
169
    $this->subOutcomeProbability = ($this->currentOutcomeConfig[self::K_ROLL_VALUE] - $this->valueRolled) / $this->currentOutcomeConfig[P_CHANCE];
170
    $this->subOutcome = $this->subOutcomeProbability >= 0.99 ? 0 : ($this->subOutcomeProbability >= 0.90 ? 1 : 2);
171
    $this->gainShare  = !empty($this->currentOutcomeConfig['percent'][$this->subOutcome])
172
      ? $this->currentOutcomeConfig['percent'][$this->subOutcome]
173
      : Constants::OUTCOME_NOT_CALCULATED;
174
175
    // Outcome CAN change ONLY object properties and SHOULD NOT mess with real fleet values
176
    switch ($this->outcome) {
177
      case Constants::OUTCOME_NONE:
178
        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;
179
      break;
180
181
      case Constants::EXPEDITION_OUTCOME_LOST_FLEET:
182
        $this->outcomeShipsLostPartially();
183
        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;
184
      break;
185
186
      case Constants::EXPEDITION_OUTCOME_LOST_FLEET_ALL:
187
        $this->outcomeLostFleetAll();
188
        $this->subOutcome = Constants::OUTCOME_NOT_CALCULATED;
189
      break;
190
191
      case Constants::EXPEDITION_OUTCOME_FOUND_FLEET:
192
        $this->outcomeFoundShips();
193
      break;
194
195
      case Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES:
196
        $this->outcomeFoundResources();
197
      break;
198
199
      case Constants::EXPEDITION_OUTCOME_FOUND_DM:
200
        $this->outcomeFoundDm();
201
      break;
202
203
      //case FLT_EXPEDITION_OUTCOME_FOUND_ARTIFACT:
204
      //break;
205
206
      default:
207
      break;
208
    }
209
210
    // Calling extra
211
    $this->flt_mission_explore_addon($this);
212
213
    // Applying expedition changes to real fleet data
214
    $this->applyFleetChanges();
215
216
    // Saving expedition result to DB
217
    $this->saveResult();
218
219
    // Sending expedition report to player
220
    $this->sendReport($this->fleetEvent->fleet);
221
222
    return CACHE_FLEET | CACHE_USER_SRC;
223
  }
224
225
  /**
226
   * @param array $theFleet
227
   *
228
   * @return string
229
   */
230
  protected function renderUnits(array $theFleet) {
231
    $add = '';
232
    foreach ($theFleet as $ship_id => $ship_amount) {
233
      $add .= SN::$lang['tech'][$ship_id] . ' - ' . $ship_amount . "\r\n";
234
    }
235
236
    return $add;
237
  }
238
239
  /**
240
   * @param static $outcome
241
   *
242
   * @return static
243
   */
244
  protected function flt_mission_explore_addon(MissionExploreResult $outcome) {
245
    /** @see core_festival::expedition_result_adjust(), FestivalActivityPuzzleExpedition::fleet_explore_adjust_result() */
246
    return sn_function_call(Constants::HOOK_MISSION_EXPLORE_ADDON, [$outcome]);
247
  }
248
249
  /**
250
   * Reset current expedition state
251
   *
252
   * @param ?FleetDispatchEvent $fleetEvent
253
   *
254
   * @return void
255
   */
256
  protected function resetExpedition(FleetDispatchEvent $fleetEvent = null) {
257
    $this->fleetEvent = $fleetEvent;
258
259
    // Fleet's ship list
260
    $this->ships = !empty($this->fleetEvent->fleet['fleet_array'])
261
      ? sys_unit_str2arr($this->fleetEvent->fleet['fleet_array'])
262
      : [];
263
264
    $this->shipsLost       = [];
265
    $this->shipsFound      = [];
266
    $this->resourcesFound  = [];
267
    $this->darkMatterFound = 0;
268
269
    $this->valueRolled           = -1;
270
    $this->outcome               = Constants::OUTCOME_NOT_CALCULATED;
271
    $this->subOutcomeProbability = -1;
272
    $this->subOutcome            = Constants::OUTCOME_NOT_CALCULATED;
273
    $this->gainShare             = 0;
274
275
    $this->fleetCapacityFree = 0;
276
    $this->shipsCostInMetal  = 0;
277
  }
278
279
  /**
280
   * Get data for ships
281
   *
282
   * @return array[]
283
   */
284
  protected static function getShipData() {
285
    if (empty(static::$shipData)) {
286
      foreach (sn_get_groups('fleet') as $unit_id) {
287
        $unit_info = get_unit_param($unit_id);
288
        if ($unit_info[P_UNIT_TYPE] != UNIT_SHIPS || empty($unit_info['engine'][0]['speed'])) {
289
          continue;
290
        }
291
        $unit_info[P_COST_METAL] = get_unit_cost_in($unit_info[P_COST]);
292
293
        static::$shipData[$unit_id] = $unit_info;
294
      }
295
    }
296
297
    return static::$shipData;
298
  }
299
300
  /**
301
   * Get resource exchange rates
302
   *
303
   * @return float[]
304
   */
305
  protected static function getExchangeRates() {
306
    if (empty(static::$rates)) {
307
      static::$rates = SN::$gc->economicHelper->getResourcesExchange();
308
    }
309
310
    return static::$rates;
311
  }
312
313
  /**
314
   * Saving expedition results to DB
315
   *
316
   * @return void
317
   */
318
  protected function saveResult() {
319
    // Increasing expedition XP - 1 point per Expedition
320
    db_user_set_by_id($this->fleetEvent->fleetOwnerId, "`player_rpg_explore_xp` = `player_rpg_explore_xp` + 1");
0 ignored issues
show
Deprecated Code introduced by
The function db_user_set_by_id() has been deprecated. ( Ignorable by Annotation )

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

320
    /** @scrutinizer ignore-deprecated */ db_user_set_by_id($this->fleetEvent->fleetOwnerId, "`player_rpg_explore_xp` = `player_rpg_explore_xp` + 1");
Loading history...
321
    // Saving changed data to DB
322
    if ($this->darkMatterFound >= 1) {
323
      rpg_points_change($this->fleetEvent->fleetOwnerId, RPG_EXPEDITION, $this->darkMatterFound, 'Expedition Bonus');
0 ignored issues
show
'Expedition Bonus' of type string is incompatible with the type boolean expected by parameter $comment of rpg_points_change(). ( Ignorable by Annotation )

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

323
      rpg_points_change($this->fleetEvent->fleetOwnerId, RPG_EXPEDITION, $this->darkMatterFound, /** @scrutinizer ignore-type */ 'Expedition Bonus');
Loading history...
324
    }
325
326
    // Checking if the fleet was destroyed entirely
327
    if (($fleetAmount = array_sum($this->ships)) >= 1) {
328
      // No - some ships left
329
      $query_data =
330
        // Routing fleet to return path
331
        ['fleet_mess' => FLEET_STATUS_RETURNING]
332
        // If there were some changes to the fleet - propagating them to DB
333
        + (!empty($this->shipsLost) || !empty($this->shipsFound)
334
          ? [
335
            'fleet_amount' => $fleetAmount,
336
            'fleet_array'  => sys_unit_arr2str($this->ships),
337
          ]
338
          : []
339
        );
340
341
      $query_delta = [];
342
      // If we found some resources - adding them to cargo bays
343
      if (!empty($this->resourcesFound) && array_sum($this->resourcesFound) > 0) {
344
        $query_delta = [
345
          'fleet_resource_metal'     => $this->resourcesFound[RES_METAL],
346
          'fleet_resource_crystal'   => $this->resourcesFound[RES_CRYSTAL],
347
          'fleet_resource_deuterium' => $this->resourcesFound[RES_DEUTERIUM],
348
        ];
349
      }
350
351
      DbFleetStatic::fleet_update_set($this->fleetEvent->fleetId, $query_data, $query_delta);
352
    } else {
353
      // Fleet empty? Removing fleet from DB
354
      DbFleetStatic::db_fleet_delete($this->fleetEvent->fleetId);
355
    }
356
  }
357
358
  /**
359
   * Compile and send expedition report
360
   *
361
   * @param array $fleetRow
362
   *
363
   * @return void
364
   */
365
  protected function sendReport(array $fleetRow) {
366
    // Generating PM for user
367
    $langExpeditions = SN::$lang['flt_mission_expedition'];
368
    // Generating outcome-specific details
369
    $msg_text_addon = '';
370
    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_DM) {
371
      $msg_text_addon = $this->darkMatterFound >= 1
372
        ? sprintf($langExpeditions['found_dark_matter'], $this->darkMatterFound)
373
        : $langExpeditions['outcomes'][$this->outcome]['no_result'];
374
    }
375
376
    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_RESOURCES) {
377
      if (array_sum($this->resourcesFound) >= 1) {
378
        $msg_text_addon = $langExpeditions['found_resources'];
379
        $msg_text_addon .= $this->renderUnits($this->resourcesFound);
380
      } else {
381
        $msg_text_addon = $langExpeditions['outcomes'][$this->outcome]['no_result'];
382
      }
383
    }
384
385
    if (!empty($this->shipsLost)) {
386
      $msg_text_addon = $langExpeditions['lost_fleet'];
387
      $msg_text_addon .= $this->renderUnits($this->shipsLost);
388
    }
389
390
    if ($this->outcome == Constants::EXPEDITION_OUTCOME_FOUND_FLEET) {
391
      if (empty($this->shipsFound)) {
392
        $msg_text_addon = $langExpeditions['outcomes'][$this->outcome]['no_result'];
393
      }
394
    }
395
396
    if (!empty($this->ships) && array_sum($this->ships) >= 1) {
397
      if (!empty($this->shipsFound)) {
398
        $msg_text_addon = $langExpeditions['found_fleet'] . $this->renderUnits($this->shipsFound);
399
      }
400
    }
401
402
    $messages = $langExpeditions['outcomes'][$this->outcome]['messages'];
403
    if (
404
      // Outcome have sub-outcomes
405
      !empty($this->currentOutcomeConfig['percent'])
406
      // Some outcome rolled
407
      && $this->subOutcome >= 0
408
      // Messages are different for different outcomes
409
      && is_array($messages)
410
    ) {
411
      // Selecting messages for specific outcome
412
      $messages = &$messages[$this->subOutcome];
413
    }
414
415
    $msg_text = is_string($messages)
416
      // If we have only one variant for message - using it
417
      ? $messages
418
      // If we have several message variations - selecting one randomly
419
      : (is_array($messages) ? $messages[mt_rand(0, count($messages) - 1)] : '');
420
421
    $msg_text = sprintf(
422
        $msg_text,
423
        $this->fleetEvent->fleetId,
424
        uni_render_coordinates($fleetRow, 'fleet_end_')
425
      ) . ($msg_text_addon ? "\r\n" . $msg_text_addon : '');
426
427
    msg_send_simple_message(
428
      $this->fleetEvent->fleetOwnerId,
429
      '',
430
      $fleetRow['fleet_end_stay'],
431
      MSG_TYPE_EXPLORE,
432
      $langExpeditions['msg_sender'],
433
      $langExpeditions['msg_title'],
434
      $msg_text
435
    );
436
  }
437
438
  /**
439
   * @return void
440
   */
441
  protected function applyFleetChanges() {
442
    // Shortcut to access and change fleet data in event
443
    $fleetRow = &$this->fleetEvent->fleet;
444
445
    // Adding found ships
446
    foreach (!empty($this->shipsFound) ? $this->shipsFound : [] as $unit_id => $unit_amount) {
447
      $this->ships[$unit_id] += $unit_amount;
448
    }
449
    // Removing lost ships
450
    foreach (!empty($this->shipsLost) ? $this->shipsLost : [] as $shipLostId => $shipLostCount) {
451
      $this->ships[$shipLostId] -= $shipLostCount;
452
      if ($this->ships[$shipLostId] < 1) {
453
        unset($this->ships[$shipLostId]);
454
      }
455
    }
456
    // Adjusting ship data in real fleet record
457
    $fleetRow['fleet_amount'] = array_sum($this->ships);
458
    $fleetRow['fleet_array']  = sys_unit_arr2str($this->ships);
459
460
    // Adjusting resources data in real fleet record
461
    if (array_sum($this->resourcesFound) >= 1) {
462
      $fleetRow['fleet_resource_metal']     += $this->resourcesFound[RES_METAL];
463
      $fleetRow['fleet_resource_crystal']   += $this->resourcesFound[RES_CRYSTAL];
464
      $fleetRow['fleet_resource_deuterium'] += $this->resourcesFound[RES_DEUTERIUM];
465
    }
466
467
    // Setting fleet to return route
468
    $fleetRow['fleet_mess'] = FLEET_STATUS_RETURNING;
469
  }
470
471
  /**
472
   * Calculating fleet cost in metal and free capacity
473
   *
474
   * @return void
475
   */
476
  protected function calcSecondaryData() {
477
    // Calculating ship's free capacity and fleet cost in metal
478
    foreach ($this->ships as $ship_id => $ship_amount) {
479
      $this->fleetCapacityFree += $ship_amount * static::$shipData[$ship_id][P_CAPACITY];
480
      $this->shipsCostInMetal  += $ship_amount * static::$shipData[$ship_id][P_COST_METAL];
481
    }
482
    // Calculating rest of fleet capacity - room which not occupied with resources
483
    $this->fleetCapacityFree = max(
484
      0,
485
      $this->fleetCapacityFree
486
      - $this->fleetEvent->fleet['fleet_resource_metal']
487
      - $this->fleetEvent->fleet['fleet_resource_crystal']
488
      - $this->fleetEvent->fleet['fleet_resource_deuterium']
489
    );
490
  }
491
492
  /**
493
   * @param array $outcomeConfigs
494
   *
495
   * @return array
496
   */
497
  protected function adjustNoneChance(array $outcomeConfigs) {
498
    // Calculating how many hours spent in expedition
499
    $flt_stay_hours =
500
      ($this->fleetEvent->fleet['fleet_end_stay'] - $this->fleetEvent->fleet['fleet_start_time']) / 3600
501
      * (SN::$config->game_speed_expedition ?: 1);
502
    // Adjusting chance for empty outcome - expedition found nothing
503
    $outcomeConfigs[Constants::OUTCOME_NONE][P_CHANCE] = ceil(Constants::OUTCOME_EXPEDITION_NOTHING_DEFAULT_CHANCE / max(0.1, pow($flt_stay_hours, 1 / 1.7)));
504
505
    return $outcomeConfigs;
506
  }
507
508
  /**
509
   * @param array $outcomeConfigs
510
   *
511
   * @return array{0: int, 1: array}
512
   */
513
  protected function calculateRollValues(array $outcomeConfigs) {
514
    // Calculating max chance can be rolled for current expedition
515
    $chance_max = 0;
516
    foreach ($outcomeConfigs as $key => &$outcomeConfig) {
517
      // Removing invalid outcomes - with no chances set or zero chances
518
      if (empty($outcomeConfig[P_CHANCE])) {
519
        unset($outcomeConfigs[$key]);
520
        continue;
521
      }
522
      $outcomeConfig[self::K_ROLL_VALUE] = $chance_max = $outcomeConfig[P_CHANCE] + $chance_max;
523
    }
524
525
    return [$outcomeConfigs, $chance_max];
526
  }
527
528
  /**
529
   * Outcome - ships partially lost
530
   *
531
   * @return void
532
   */
533
  protected function outcomeShipsLostPartially() {
534
    // 1-3 pack of 20-30%% -> 20-90%% lost totally
535
    // Calculating lost share per fleet to maintain mathematical consistency for math model
536
    $lostShare = mt_rand(1, 3) * (mt_rand(200000, 300000) / CONST_1M);
537
    foreach ($this->ships as $shipId => $shipCount) {
538
      $this->shipsLost[$shipId] = ceil($shipCount * $lostShare);
539
    }
540
  }
541
542
  /**
543
   * Outcome - lost all fleet
544
   *
545
   * @return void
546
   */
547
  protected function outcomeLostFleetAll() {
548
    foreach ($this->ships as $shipsId => $shipCount) {
549
      $this->shipsLost[$shipsId] += $this->ships[$shipsId];
550
    }
551
  }
552
553
  /**
554
   * @return void
555
   */
556
  protected function outcomeFoundResources() {
557
    // Calculating found resources amount in metal
558
    $found_in_metal = ceil(
559
      min($this->gainShare * $this->shipsCostInMetal, game_resource_multiplier(true) * CONST_10M, $this->fleetCapacityFree)
560
      // 95-105%% [0.95 - 1.05]
561
      * (mt_rand(95 * 10000, 105 * 10000) / CONST_1M)
562
    ); // game_speed
563
564
    // 30-70%% of resources found are found in metal. Large numbers used to add more variability
565
    $this->resourcesFound[RES_METAL] = floor($found_in_metal * mt_rand(3 * CONST_100K, 7 * CONST_100K) / CONST_1M);
566
    // Deducing found metal from pool
567
    $found_in_metal -= $this->resourcesFound[RES_METAL];
568
569
    // Converting rest of found metal to crystals. Large numbers used to add more variability
570
    $found_in_metal = floor($found_in_metal * static::$rates[RES_METAL] / static::$rates[RES_CRYSTAL]);
571
    // 50-100%% of rest resources are found in crystals
572
    $this->resourcesFound[RES_CRYSTAL] = floor($found_in_metal * mt_rand(5 * CONST_100K, 10 * CONST_100K) / CONST_1M);
573
    // Deducing found crystals from pool
574
    $found_in_metal -= $this->resourcesFound[RES_CRYSTAL];
575
576
    // Converting rest of found crystals to deuterium
577
    $found_in_metal = floor($found_in_metal * static::$rates[RES_CRYSTAL] / static::$rates[RES_DEUTERIUM]);
578
    // 100% of resources rest are in deuterium
579
    $this->resourcesFound[RES_DEUTERIUM] = $found_in_metal;
580
  }
581
582
  /**
583
   *
584
   * @return void
585
   */
586
  protected function outcomeFoundShips() {
587
    // Рассчитываем эквивалент найденного флота в метале
588
    $found_in_metal = min($this->gainShare * $this->shipsCostInMetal, game_resource_multiplier(true) * CONST_10M);
589
    //  13 243 754 000 g x1
590
    //  60 762 247 000 a x10
591
    // 308 389 499 488 000 b x500
592
593
    // Рассчитываем стоимость самого дорого корабля в металле
594
    $shipMaxCostInMetal = 0;
595
    foreach ($this->ships as $ship_id => $ship_amount) {
596
      $shipMaxCostInMetal = max($shipMaxCostInMetal, static::$shipData[$ship_id][P_COST_METAL]);
597
    }
598
599
    // Ограничиваем корабли только теми, чья стоимость в металле меньше или равно стоимости самого дорогого корабля
600
    $can_be_found = [];
601
602
    foreach (static::$shipData as $ship_id => $ship_info) {
603
      if (
604
        $ship_info[P_COST_METAL] <= $shipMaxCostInMetal
605
        // and not race ship
606
        && empty($ship_info[P_RACE_SHIP])
607
        // and not event-related ship
608
        && empty($ship_info[P_REQUIRE_HIGHSPOT])
609
      ) {
610
        $can_be_found[$ship_id] = $ship_info[P_COST_METAL];
611
      }
612
    }
613
614
    // Убираем колонизаторы и шпионов - миллиарды шпионов и колонизаторов нам не нужны
615
    unset($can_be_found[SHIP_COLONIZER]);
616
    unset($can_be_found[SHIP_SPY]);
617
618
    while (count($can_be_found) && $found_in_metal >= max($can_be_found)) {
619
      $found_index     = mt_rand(1, count($can_be_found)) - 1;
620
      $found_ship      = array_slice($can_be_found, $found_index, 1, true);
621
      $found_ship_cost = reset($found_ship);
622
      $found_ship_id   = key($found_ship);
623
624
      if ($found_ship_cost > $found_in_metal) {
625
        unset($can_be_found[$found_ship_id]);
626
      } else {
627
        $found_ship_count                 = mt_rand(1, floor($found_in_metal / $found_ship_cost));
0 ignored issues
show
floor($found_in_metal / $found_ship_cost) of type double is incompatible with the type integer expected by parameter $max of mt_rand(). ( Ignorable by Annotation )

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

627
        $found_ship_count                 = mt_rand(1, /** @scrutinizer ignore-type */ floor($found_in_metal / $found_ship_cost));
Loading history...
628
        $this->shipsFound[$found_ship_id] += $found_ship_count;
629
        $found_in_metal                   -= $found_ship_count * $found_ship_cost;
630
      }
631
    }
632
  }
633
634
  /**
635
   * @return void
636
   */
637
  protected function outcomeFoundDm() {
638
    // Рассчитываем количество найденной ТМ
639
    $this->darkMatterFound = floor(
640
      min(
641
        $this->gainShare * $this->shipsCostInMetal / static::$rates[RES_DARK_MATTER],
642
        self::MAX_DM
643
      )
644
      // 75-100%% of calculated value
645
      * mt_rand(750000, CONST_1M) / CONST_1M
646
    );
647
  }
648
649
}
650