Completed
Push — work-fleets ( e7900f...9522d3 )
by SuperNova.WS
05:39
created

UnitList   C

Complexity

Total Complexity 78

Size/Duplication

Total Lines 535
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 15
Bugs 0 Features 0
Metric Value
wmc 78
c 15
b 0
f 0
lcom 1
cbo 9
dl 0
loc 535
rs 5.4563

29 Methods

Rating   Name   Duplication   Size   Complexity  
A getPlayerOwnerId() 0 3 2
A getLocationType() 0 3 2
A getLocationDbId() 0 3 2
A setLocatedAt() 0 7 1
A getLocatedAt() 0 3 1
A getLocatedAtType() 0 3 2
A getLocatedAtDbId() 0 3 2
A offsetSet() 0 7 2
A offsetUnset() 0 9 2
B dbLoad() 0 29 5
B dbSave() 0 25 6
A _createElement() 0 6 1
A unitSetCount() 0 3 1
A unitGetCount() 0 6 2
A unitAdjustCount() 0 14 3
A unitsGetArray() 0 8 2
A unitsCountApplyLossMultiplier() 0 5 2
A unitsCount() 0 3 1
A unitsCapacity() 0 3 1
A unitsCountById() 0 3 1
A unitsPropertySumById() 0 10 4
B unitsRender() 0 37 6
B flt_fleet_speed() 0 13 6
D travelData() 0 39 9
A unitsInGroup() 0 9 3
A unitsIsAllMovable() 0 10 3
B _dump() 0 97 2
A unitZeroDbId() 0 5 2
A unitZeroCount() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like UnitList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UnitList, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Class UnitList
5
 * Indexed by DB_ID - as it should be!
6
 *
7
 *
8
 *
9
 * Hints for IDE - inherited from ArrayAccessV2
10
 *
11
 * @method Unit offsetGet($offset)
12
 * @property Unit[] $_container
13
 *
14
 */
15
class UnitList extends ArrayAccessV2 implements IDbRow, ILocation {
16
17
18
  // Properties ********************************************************************************************************
19
20
  // ILocation implementation ==========================================================================================
21
22
  /**
23
   * Type of this location
24
   *
25
   * @var int $locationType
26
   */
27
  protected static $locationType = LOC_UNIT_LIST;
28
  /**
29
   * @var ILocation $locatedAt
30
   */
31
  protected $locatedAt = null;
32
33
34
  // New properties ====================================================================================================
35
36
  /**
37
   * @var Unit[] $mapUnitIdToDb
38
   */
39
  // Нужно для корректного сохранения новых юнитов. Их db_id = 0, поэтому при добавлении в контейнер они будут перезаписывать друг друга
40
  // Соответственно - при сохраненнии флота надо проходить dbSave именно по $mapUnitIdToDb
41
  protected $mapUnitIdToDb = array();
42
43
44
  // Methods ***********************************************************************************************************
45
46
  // ILocation implementation ==========================================================================================
47
48
  public function getPlayerOwnerId() {
49
    return is_object($this->locatedAt) ? $this->locatedAt->getPlayerOwnerId() : null;
50
  }
51
52
  public function getLocationType() {
53
    return is_object($this->locatedAt) ? $this->locatedAt->getLocationType() : LOC_NONE;
54
  }
55
56
  public function getLocationDbId() {
57
    return is_object($this->locatedAt) ? $this->locatedAt->getLocationDbId() : null;
58
  }
59
60
  // TODO - достаточно установить один раз Unit::LocatedAt на UnitList, что бы затем все юниты автоматически брали наиболее актуальный locatedAt
61
  public function setLocatedAt($location) {
62
    $this->locatedAt = $location;
63
    // TODO - по факту не нужно - достточно один раз поставить на $this
64
//    foreach($this->_container as $unit) {
65
//      $unit->setLocatedAt($this->locatedAt);
66
//    }
67
  }
68
69
  public function getLocatedAt() {
70
    return $this->locatedAt;
71
  }
72
73
  public function getLocatedAtType() {
74
    return is_object($this->locatedAt) ? $this->locatedAt->getLocationType() : LOC_NONE;
75
  }
76
77
  public function getLocatedAtDbId() {
78
    return is_object($this->locatedAt) ? $this->locatedAt->getLocationDbId() : 0;
79
  }
80
81
82
  // ArrayAccessV2 inheritance =========================================================================================
83
84
  /**
85
   * Adds link to unit object also to mapUnitIdToDb
86
   *
87
   * @param mixed $offset
88
   * @param Unit  $value
89
   */
90
  public function offsetSet($offset, $value) {
91
    if(isset($this->mapUnitIdToDb[$value->unitId])) {
92
      classSupernova::$debug->error('UnitList::offsetSet: Unit with UnitId ' . $value->unitId . ' already exists');
93
    }
94
    $this->mapUnitIdToDb[$value->unitId] = $value;
95
    parent::offsetSet($offset, $value);
96
  }
97
98
  public function offsetUnset($offset) {
99
    if(!empty($this[$offset]->unitId)) {
100
//      $unit_id = $this[$offset]->unitId;
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
101
//      $this->mapUnitIdToDb[$unit_id] = null;
102
//      unset($this->mapUnitIdToDb[$unit_id]);
103
      unset($this->mapUnitIdToDb[$this[$offset]->unitId]);
104
    }
105
    parent::offsetUnset($offset);
106
  }
107
108
109
  // IDbRow implementation =============================================================================================
110
111
  /**
112
   * Loading object from DB by primary ID
113
   * Real location should be set before calling this method
114
   *
115
   * @param int $dbId - dbId is generally unused here. However it works as flag: 0 - just reset; (negative) - just reset; (positive) - proceed with loading
116
   */
117
  public function dbLoad($dbId, $lockSkip = false) {
118
//    $this->_reset();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
119
120
    if($dbId <= 0) {
121
      return;
122
    }
123
124
    if(!is_object($this->locatedAt)) {
125
      classSupernova::$debug->error('UnitList::dbLoad have no locatedAt field set');
126
    }
127
128
    $unit_array = classSupernova::db_get_unit_list_by_location(0, $this->getLocationType(), $this->getLocationDbId());
129
    if(!is_array($unit_array)) {
130
      return;
131
    }
132
133
    foreach($unit_array as $unit_db_row) {
134
      $unit = $this->_createElement();
135
      $unit->dbRowParse($unit_db_row);
136
137
      // TODO - сюда вставить разборку бонусов данного юнитлиста - тех бонусов, которые Grants данный юнит добавить в список бонусов юнит-листа
138
139
      $this[$unit->dbId] = $unit;
140
    }
141
142
    // TODO - Применить бонусы от location
143
    // Точнее - опустить бонусы с юнитлиста (те, которые Grants) на каждый юнит (те, которые receives)
144
    // Вообще-то Receives это будут параметры каждого юнита
145
  }
146
147
  public function dbSave() {
148
    if(!is_object($this->locatedAt)) {
149
      classSupernova::$debug->error('UnitList::dbSave have no locatedAt field set');
150
    }
151
152
    foreach($this->mapUnitIdToDb as $unit) {
153
      $unit_db_id = $unit->dbId;
154
      $unit->dbSave();
155
156
      if($unit->isEmpty()) {
157
        // Removing unit object
158
        // TODO - change when there will be common bus for all objects
159
        // ...or should I? If COUNT is empty - it means that object does not exists in DB. So it should be deleted from PHP memory and cache too
160
        unset($this[$unit_db_id]);
161
      } else {
162
        if($unit->dbId <= 0) {
163
          classSupernova::$debug->error('Error writing unit to DB');
164
        }
165
        // If unit is new then putting unit object to container
166
        if(empty($this->_container[$unit->dbId])) {
167
          $this->_container[$unit->dbId] = $unit;
168
        }
169
      }
170
    }
171
  }
172
173
174
175
176
177
  // Other =============================================================================================================
178
179
  /**
180
   * @return Unit
181
   *
182
   * @version 41a6.76
183
   */
184
  // TODO - Factory
185
  public function _createElement() {
186
    $unit = new Unit();
187
    $unit->setLocatedAt($this);
188
189
    return $unit;
190
  }
191
192
  /**
193
   * Set unit count of $unit_id to $unit_count
194
   * If there is no $unit_id - it will be created and saved to DB on dbSave
195
   *
196
   * @param int $unit_id
197
   * @param int $unit_count
198
   */
199
  public function unitSetCount($unit_id, $unit_count = 0) {
200
    $this->unitAdjustCount($unit_id, $unit_count, true);
201
  }
202
203
  public function unitGetCount($unit_id) {
204
    if(empty($this->mapUnitIdToDb[$unit_id])) {
205
      throw new Exception('Unit [' . $unit_id . '] is not exists in UnitList');
206
    }
207
    return $this->mapUnitIdToDb[$unit_id]->count;
208
  }
209
210
  /**
211
   * Adjust unit count of $unit_id by $unit_count - or just replace value
212
   * If there is no $unit_id - it will be created and saved to DB on dbSave
213
   *
214
   * @param int  $unit_id
215
   * @param int  $unit_count
216
   * @param bool $replace_value
217
   */
218
  public function unitAdjustCount($unit_id, $unit_count = 0, $replace_value = false) {
219
    if(empty($this->mapUnitIdToDb[$unit_id])) {
220
      // If unit not exists - creating one and setting all attributes
221
      $this->mapUnitIdToDb[$unit_id] = $this->_createElement();
222
      $this->mapUnitIdToDb[$unit_id]->setUnitId($unit_id);
223
      $this->mapUnitIdToDb[$unit_id]->setLocatedAt($this);
224
    }
225
226
    if($replace_value) {
227
      $this->mapUnitIdToDb[$unit_id]->count = $unit_count;
228
    } else {
229
      $this->mapUnitIdToDb[$unit_id]->adjustCount($unit_count);
230
    }
231
  }
232
233
  /**
234
   * Get unit list in array as $unit_id => $unit_count
235
   *
236
   * @return array
237
   */
238
  public function unitsGetArray() {
239
    $result = array();
240
    foreach($this->mapUnitIdToDb as $unit) {
241
      $result[$unit->unitId] = $unit->count;
242
    }
243
244
    return $result;
245
  }
246
247
  public function unitsCountApplyLossMultiplier($ships_lost_multiplier) {
248
    foreach($this->mapUnitIdToDb as $unit_id => $unit) {
249
      $unit->count = floor($unit->count * $ships_lost_multiplier);
250
    }
251
  }
252
253
  public function unitsCount() {
254
    return $this->unitsPropertySumById(0, 'count');
255
  }
256
257
  public function unitsCapacity() {
258
    return $this->unitsPropertySumById(0, 'capacity');
259
  }
260
261
  /**
262
   * Get count of units in UnitList by unit_id (or all units if unit_id == 0)
263
   *
264
   * @param int $unit_id - 0 - all units
265
   *
266
   * @return int
267
   */
268
  public function unitsCountById($unit_id = 0) {
269
    return $this->unitsPropertySumById($unit_id, 'count');
270
  }
271
272
  public function unitsPropertySumById($unit_id = 0, $propertyName = 'count') {
273
    $result = 0;
274
    foreach($this->mapUnitIdToDb as $unit) {
275
      if(!$unit_id || $unit->unitId == $unit_id) {
276
        $result += $unit->$propertyName;
277
      }
278
    }
279
280
    return $result;
281
  }
282
283
  /**
284
   * @param $page
285
   * @param $fleet
286
   * @param template $template
287
   *
288
   * @throws Exception
289
   */
290
  public function unitsRender(&$page, &$fleet, &$template) {
291
    /**
292
     * @var Fleet $objFleet
293
     */
294
    $objFleet = $this->getLocatedAt();
295
    if(empty($objFleet)) {
296
      throw new Exception('No fleet owner on UnitList::unitsRender() in ' . __FILE__ . '@' . __LINE__);
297
    }
298
299
    foreach($this->mapUnitIdToDb as $unit) {
300
      $ship_id = $unit->unitId;
301
      $ship_count = $unit->count;
302
      if(!UnitShip::is_in_group($ship_id) || $ship_count <= 0) {
303
        continue;
304
      }
305
306
      if($ship_count > mrc_get_level($objFleet->dbOwnerRow, $objFleet->dbSourcePlanetRow, $ship_id, false, true)) {
307
        $page .= classLocale::$lang['fl_noenought'];
308
      } else {
309
        $fleet['fleetarray'][$ship_id] = $ship_count;
310
      }
311
312
      $ship_info = get_unit_param($ship_id);
313
      $fleet['capacity'] += $ship_info[P_CAPACITY] * $ship_count;
314
      $ship_base_data = get_ship_data($ship_id, $objFleet->dbOwnerRow);
315
      $template->assign_block_vars('fleets.ships', array(
316
        'ID'          => $ship_id,
317
        'AMOUNT'      => $ship_count,
318
        'AMOUNT_TEXT' => pretty_number($ship_count),
319
        'CONSUMPTION' => $ship_base_data['consumption'],
320
        'SPEED'       => $ship_base_data['speed'],
321
        'NAME'        => classLocale::$lang['tech'][$ship_id],
322
      ));
323
    }
324
325
    $fleet['amount'] += $this->unitsCount();
326
  }
327
328
329
  /**
330
   * @param $user
331
   *
332
   * @return int|mixed
333
   */
334
  // TODO - REDO!!!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
335
  function flt_fleet_speed($user) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
336
    $speeds = array();
337
    if(!empty($this->mapUnitIdToDb)) {
338
      foreach($this->mapUnitIdToDb as $ship_id => $unit) {
339
        if($unit->getCount() > 0 && in_array($unit->unitId, sn_get_groups(array('fleet', 'missile')))) {
340
          $single_ship_data = get_ship_data($unit->unitId, $user);
341
          $speeds[] = $single_ship_data['speed'];
342
        }
343
      }
344
    }
345
346
    return empty($speeds) ? 0 : min($speeds);
347
  }
348
349
  public function travelData($speed_percent = 10, $distance, $dbOwnerRow) {
350
    $consumption = 0;
351
    $capacity = 0;
352
    $duration = 0;
353
354
    $speed_percent = $speed_percent ? max(min($speed_percent, 10), 1) : 10;
355
356
    $game_fleet_speed = flt_server_flight_speed_multiplier();
357
    $fleet_speed = $this->flt_fleet_speed($dbOwnerRow);
358
    $real_speed = $speed_percent * sqrt($fleet_speed);
359
360
    if($fleet_speed && $game_fleet_speed) {
361
      $duration = max(1, round((35000 / $speed_percent * sqrt($distance * 10 / $fleet_speed) + 10) / $game_fleet_speed));
362
363
      foreach($this->mapUnitIdToDb as $ship_id => $unit) {
364
        if(!$unit->unitId || $unit->getCount() <= 0) {
365
          continue;
366
        }
367
368
        $single_ship_data = get_ship_data($unit->unitId, $dbOwnerRow);
369
        $single_ship_data['speed'] = $single_ship_data['speed'] < 1 ? 1 : $single_ship_data['speed'];
370
371
        $consumption += $single_ship_data['consumption'] * $unit->getCount() * pow($real_speed / sqrt($single_ship_data['speed']) / 10 + 1, 2);
372
        $capacity += $single_ship_data['capacity'] * $unit->getCount();
373
      }
374
375
      $consumption = round($distance * $consumption / 35000) + 1;
376
    }
377
378
    return array(
379
      'fleet_speed'            => $fleet_speed,
380
      'distance'               => $distance,
381
      'duration'               => $duration,
382
      'consumption'            => $consumption,
383
      'capacity'               => $capacity,
384
      'hold'                   => $capacity - $consumption,
385
      'transport_effectivness' => $consumption ? $capacity / $consumption : 0,
386
    );
387
  }
388
389
  /**
390
   * @param $group
391
   *
392
   * @return bool
393
   */
394
  public function unitsInGroup($group) {
395
    foreach($this->mapUnitIdToDb as $unitId => $unit) {
396
      if(!in_array($unitId, $group)) {
397
        return false;
398
      }
399
    }
400
401
    return true;
402
  }
403
404
  public function unitsIsAllMovable($dbOwnerRow) {
405
    foreach($this->mapUnitIdToDb as $unitId => $unit) {
406
      $single_ship_data = get_ship_data($unit->unitId, $dbOwnerRow);
407
      if($single_ship_data['speed'] <= 0) {
408
        return false;
409
      }
410
    }
411
412
    return true;
413
  }
414
415
416
//  // TODO - revise it later
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
417
//  public function _reset() {
418
//    //if(!empty($this->mapUnitIdToDb)) {
419
//    //  foreach($this->mapUnitIdToDb as $unit_id => $object) {
420
//    //    unset($this->mapUnitIdToDb[$unit_id]);
421
//    //  }
422
//    //}
423
//    unset($this->mapUnitIdToDb);
424
//    $this->mapUnitIdToDb = array();
425
//
426
//    //if(!empty($this->_container)) {
427
//    //  foreach($this->_container as $unit_db_id => $object) {
428
//    //    unset($this->_container[$unit_db_id]);
429
//    //  }
430
//    //}
431
//    unset($this->_container);
432
//    $this->_container = array();
433
//  }
434
435
436
  // TODO - DEBUG - REMOVE =============================================================================================
437
  public function _dump() {
438
    print(__FILE__ . ':' . __LINE__ . "<br />");
439
    print("Located at " . $this->getLocationDbId() . " type " . $this->getLocationType() . "<br />");
440
441
    print('<table border="1">');
442
    print('<tr>');
443
444
    print('<th>');
445
    print('dbId');
446
    print('</th>');
447
448
    print('<th>');
449
    print('type');
450
    print('</th>');
451
452
    print('<th>');
453
    print('unitId');
454
    print('</th>');
455
456
    print('<th>');
457
    print('count');
458
    print('</th>');
459
460
    print('<th>');
461
    print('playerOwnerId');
462
    print('</th>');
463
464
    print('<th>');
465
    print('location');
466
    print('</th>');
467
468
    print('<th>');
469
    print('locationType');
470
    print('</th>');
471
472
    print('<th>');
473
    print('locationDbId');
474
    print('</th>');
475
476
    print('<th>');
477
    print('timeStart');
478
    print('</th>');
479
480
    print('<th>');
481
    print('timeFinish');
482
    print('</th>');
483
484
    print('</tr>');
485
486
    foreach($this->mapUnitIdToDb as $unit) {
487
      print('<tr>');
488
489
      print('<td>');
490
      print($unit->dbId);
491
      print('</td>');
492
493
      print('<td>');
494
      $type = $unit->getType();
495
      print("[{$type}] " . classLocale::$lang['tech'][$type]);
496
      print('</td>');
497
498
      print('<td>');
499
      print("[{$unit->unitId}] " . classLocale::$lang['tech'][$unit->unitId]);
500
      print('</td>');
501
502
      print('<td>');
503
      print($unit->count);
504
      print('</td>');
505
506
      print('<td>');
507
      print($unit->getPlayerOwnerId());
508
      print('</td>');
509
510
      print('<td>');
511
//      print($unit->location);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
512
      print('</td>');
513
514
      print('<td>');
515
      print($unit->getLocationType());
516
      print('</td>');
517
518
      print('<td>');
519
      print($unit->getLocationDbId());
520
      print('</td>');
521
522
      print('<td>');
523
      print($unit->getTimeStart());
524
      print('</td>');
525
526
      print('<td>');
527
      print($unit->getTimeFinish());
528
      print('</td>');
529
530
      print('</tr>');
531
    }
532
    print('</table>');
533
  }
534
535
536
  public function unitZeroDbId() {
537
    foreach($this->mapUnitIdToDb as $unit) {
538
      $unit->zeroDbId();
539
    }
540
  }
541
542
543
  public function unitZeroCount() {
544
    foreach($this->mapUnitIdToDb as $unit) {
545
      $unit->count = 0;
546
    }
547
  }
548
549
}
550