• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CleverRaven / Cataclysm-DDA / 13043

pending completion
13043

Pull #26689

travis-ci

web-flow
Remove weird adjustment to small jack

This isn't a "crazy" feature, just a broken one.
Pull Request #26689: Remove weird adjustment to small jack

31467 of 118130 relevant lines covered (26.64%)

98032.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.64
/src/vehicle_part.cpp
1
#include "vehicle.h"
2

3
#include "coordinate_conversions.h"
4
#include "debug.h"
5
#include "game.h"
6
#include "item.h"
7
#include "itype.h"
8
#include "map.h"
9
#include "messages.h"
10
#include "npc.h"
11
#include "output.h"
12
#include "string_formatter.h"
13
#include "translations.h"
14
#include "veh_type.h"
15
#include "vpart_position.h"
16
#include "weather.h"
17

18
#include <algorithm>
19
#include <cassert>
20
#include <cmath>
21
#include <set>
22

23
static const itype_id fuel_type_none( "null" );
1✔
24
static const itype_id fuel_type_battery( "battery" );
1✔
25
/*-----------------------------------------------------------------------------
26
 *                              VEHICLE_PART
27
 *-----------------------------------------------------------------------------*/
28
vehicle_part::vehicle_part()
×
29
    : mount( 0, 0 ), id( vpart_id::NULL_ID() ) {}
×
30

31
vehicle_part::vehicle_part( const vpart_id &vp, const point dp, item &&obj )
8,375✔
32
    : mount( dp ), id( vp ), base( std::move( obj ) )
50,250✔
33
{
34
    // Mark base item as being installed as a vehicle part
35
    base.item_tags.insert( "VEHICLE" );
8,375✔
36

37
    if( base.typeId() != vp->item ) {
8,375✔
38
        debugmsg( "incorrect vehicle part item, expected: %s, received: %s",
×
39
                  vp->item.c_str(), base.typeId().c_str() );
×
40
    }
41
}
8,375✔
42

43
vehicle_part::operator bool() const
×
44
{
45
    return id != vpart_id::NULL_ID();
×
46
}
47

48
const item &vehicle_part::get_base() const
×
49
{
50
    return base;
×
51
}
52

53
void vehicle_part::set_base( const item &new_base )
×
54
{
55
    base = new_base;
×
56
}
57

58
item vehicle_part::properties_to_item() const
×
59
{
60
    item tmp = base;
×
61
    tmp.item_tags.erase( "VEHICLE" );
×
62

63
    // Cables get special handling: their target coordinates need to remain
64
    // stored, and if a cable actually drops, it should be half-connected.
65
    if( tmp.has_flag( "CABLE_SPOOL" ) ) {
×
66
        tripoint local_pos = g->m.getlocal( target.first );
×
67
        if( !g->m.veh_at( local_pos ) ) {
×
68
            tmp.item_tags.insert( "NO_DROP" ); // That vehicle ain't there no more.
×
69
        }
70

71
        tmp.set_var( "source_x", target.first.x );
×
72
        tmp.set_var( "source_y", target.first.y );
×
73
        tmp.set_var( "source_z", target.first.z );
×
74
        tmp.set_var( "state", "pay_out_cable" );
×
75
        tmp.active = true;
×
76
    }
77

78
    return tmp;
×
79
}
80

81
std::string vehicle_part::name() const
290✔
82
{
83
    auto res = info().name();
290✔
84

85
    if( base.engine_displacement() > 0 ) {
290✔
86
        res.insert( 0, string_format( _( "%2.1fL " ), base.engine_displacement() / 100.0 ) );
4✔
87

88
    } else if( wheel_diameter() > 0 ) {
286✔
89
        res.insert( 0, string_format( _( "%d\" " ), wheel_diameter() ) );
16✔
90
    }
91

92
    if( base.is_faulty() ) {
290✔
93
        res += ( _( " (faulty)" ) );
×
94
    }
95

96
    if( base.has_var( "contained_name" ) ) {
290✔
97
        res += string_format( _( " holding %s" ), base.get_var( "contained_name" ) );
×
98
    }
99
    return res;
290✔
100
}
101

102
int vehicle_part::hp() const
173✔
103
{
104
    int dur = info().durability;
173✔
105
    if( base.max_damage() > 0 ) {
173✔
106
        return dur - ( dur * base.damage() / base.max_damage() );
173✔
107
    } else {
108
        return dur;
109
    }
110
}
111

112
int vehicle_part::damage() const
×
113
{
114
    return base.damage();
×
115
}
116

117
int vehicle_part::damage_level( int max ) const
×
118
{
119
    return base.damage_level( max );
×
120
}
121

122
double vehicle_part::health_percent() const
654,125✔
123
{
124
    return ( 1.0 - static_cast<double>( base.damage() ) / base.max_damage() );
654,125✔
125
}
126

127
double vehicle_part::damage_percent() const
×
128
{
129
    return static_cast<double>( base.damage() ) / base.max_damage();
×
130
}
131

132
/** parts are considered broken at zero health */
133
bool vehicle_part::is_broken() const
4,577,182✔
134
{
135
    return base.damage() >= base.max_damage();
4,577,182✔
136
}
137

138
bool vehicle_part::is_unavailable( const bool carried ) const
2,928,942✔
139
{
140
    return is_broken() || ( has_flag( carried_flag ) && carried );
4,393,327✔
141
}
142

143
bool vehicle_part::is_available( const bool carried ) const
722,353✔
144
{
145
    return !is_unavailable( carried );
722,353✔
146
}
147

148
itype_id vehicle_part::ammo_current() const
10,453,792✔
149
{
150
    if( is_battery() ) {
10,453,792✔
151
        return "battery";
147,378✔
152
    }
153

154
    if( is_tank() && !base.contents.empty() ) {
10,558,701✔
155
        return base.contents.front().typeId();
382,206✔
156
    }
157

158
    if( is_fuel_store( false ) || is_turret() ) {
20,169,433✔
159
        return base.ammo_current();
92,579✔
160
    }
161

162
    if( is_engine() ) {
10,022,732✔
163
        return info().fuel_type != "muscle" ? info().fuel_type : "null";
124,610✔
164
    }
165

166
    return "null";
9,898,122✔
167
}
168

169
long vehicle_part::ammo_capacity() const
763✔
170
{
171
    if( is_tank() ) {
763✔
172
        return item::find_type( ammo_current() )->charges_per_volume( base.get_container_capacity() );
520✔
173
    }
174

175
    if( is_fuel_store( false ) || is_turret() ) {
243✔
176
        return base.ammo_capacity();
243✔
177
    }
178

179
    return 0;
180
}
181

182
long vehicle_part::ammo_remaining() const
317,991✔
183
{
184
    if( is_tank() ) {
317,991✔
185
        return base.contents.empty() ? 0 : base.contents.back().charges;
552,795✔
186
    }
187

188
    if( is_fuel_store( false ) || is_turret() ) {
254,104✔
189
        return base.ammo_remaining();
13,348✔
190
    }
191

192
    return 0;
193
}
194

195
int vehicle_part::ammo_set( const itype_id &ammo, long qty )
436✔
196
{
197
    const itype *liquid = item::find_type( ammo );
436✔
198
    if( is_tank() && liquid->phase >= LIQUID ) {
436✔
199
        base.contents.clear();
260✔
200
        auto stack = units::legacy_volume_factor / std::max( liquid->stack_size, 1 );
780✔
201
        long limit = units::from_milliliter( ammo_capacity() ) / stack;
780✔
202
        base.emplace_back( ammo, calendar::turn, qty >= 0 ? std::min( qty, limit ) : limit );
520✔
203
        return qty;
260✔
204
    }
205

206
    if( is_turret() ) {
176✔
207
        return base.ammo_set( ammo, qty ).ammo_remaining();
9✔
208
    }
209

210
    if( is_fuel_store() ) {
167✔
211
        base.ammo_set( ammo, qty >= 0 ? qty : ammo_capacity() );
167✔
212
        return base.ammo_remaining();
167✔
213
    }
214

215
    return -1;
216
}
217

218
void vehicle_part::ammo_unset()
5,203✔
219
{
220
    if( is_tank() ) {
5,203✔
221
        base.contents.clear();
×
222
    } else if( is_fuel_store() ) {
5,203✔
223
        base.ammo_unset();
1✔
224
    }
225
}
5,203✔
226

227
long vehicle_part::ammo_consume( long qty, const tripoint &pos )
2✔
228
{
229
    if( is_tank() && !base.contents.empty() ) {
2✔
230
        int res = std::min( ammo_remaining(), qty );
×
231
        item &liquid = base.contents.back();
×
232
        liquid.charges -= res;
×
233
        if( liquid.charges == 0 ) {
×
234
            base.contents.clear();
×
235
        }
236
        return res;
×
237
    }
238
    return base.ammo_consume( qty, pos );
2✔
239
}
240

241
float vehicle_part::consume_energy( const itype_id &ftype, float energy )
370,650✔
242
{
243
    if( base.contents.empty() || !is_fuel_store() ) {
741,300✔
244
        return 0.0f;
245
    }
246

247
    item &fuel = base.contents.back();
25,822✔
248
    if( fuel.typeId() == ftype ) {
12,911✔
249
        assert( fuel.is_fuel() );
7,174✔
250
        float energy_per_unit = fuel.fuel_energy();
7,174✔
251
        long charges_to_use = static_cast<int>( std::ceil( energy / energy_per_unit ) );
14,348✔
252
        if( charges_to_use > fuel.charges ) {
7,174✔
253
            long had_charges = fuel.charges;
40✔
254
            base.contents.clear();
40✔
255
            return had_charges * energy_per_unit;
40✔
256
        }
257

258
        fuel.charges -= charges_to_use;
7,134✔
259
        return charges_to_use * energy_per_unit;
7,134✔
260
    }
261
    return 0.0f;
262
}
263

264
bool vehicle_part::can_reload( const item &obj ) const
×
265
{
266
    // first check part is not destroyed and can contain ammo
267
    if( !is_fuel_store() ) {
×
268
        return false;
269
    }
270

271
    if( !obj.is_null() ) {
×
272
        const itype_id obj_type = obj.typeId();
×
273
        if( is_reactor() ) {
×
274
            return base.is_reloadable_with( obj_type );
×
275
        }
276

277
        // forbid filling tanks with solids or non-material things
278
        if( is_tank() && ( obj.made_of( SOLID ) || obj.made_of( PNULL ) ) ) {
×
279
            return false;
280
        }
281
        // forbid putting liquids, gasses, and plasma in things that aren't tanks
282
        else if( !obj.made_of( SOLID ) && !is_tank() ) {
×
283
            return false;
284
        }
285
        // prevent mixing of different ammo
286
        if( ammo_current() != "null" && ammo_current() != obj_type ) {
×
287
            return false;
288
        }
289
        // For storage with set type, prevent filling with different types
290
        if( info().fuel_type != fuel_type_none && info().fuel_type != obj_type ) {
×
291
            return false;
292
        }
293
        // don't fill magazines with inappropriate fuel
294
        if( !is_tank() && !base.is_reloadable_with( obj_type ) ) {
×
295
            return false;
296
        }
297
    }
298

299
    return ammo_remaining() < ammo_capacity();
×
300
}
301

302
void vehicle_part::process_contents( const tripoint &pos, const bool e_heater )
×
303
{
304
    // for now we only care about processing food containers since things like
305
    // fuel don't care about temperature yet
306
    if( base.is_food_container() ) {
×
307
        int temp = g->get_temperature( pos );
×
308
        if( e_heater ) {
×
309
            temp = std::max( temp, temperatures::cold + 1 );
×
310
        }
311
        base.process( nullptr, pos, false, temp, 1 );
×
312
    }
313
}
314

315
bool vehicle_part::fill_with( item &liquid, long qty )
×
316
{
317
    if( !is_tank() || !can_reload( liquid ) ) {
×
318
        return false;
319
    }
320

321
    base.fill_with( liquid, qty );
×
322
    return true;
×
323
}
324

325
const std::set<fault_id> &vehicle_part::faults() const
197,556✔
326
{
327
    return base.faults;
197,556✔
328
}
329

330
std::set<fault_id> vehicle_part::faults_potential() const
2✔
331
{
332
    return base.faults_potential();
3✔
333
}
334

335
bool vehicle_part::fault_set( const fault_id &f )
1✔
336
{
337
    if( !faults_potential().count( f ) ) {
2✔
338
        return false;
339
    }
340
    base.faults.insert( f );
1✔
341
    return true;
1✔
342
}
343

344
int vehicle_part::wheel_area() const
241,728✔
345
{
346
    return base.is_wheel() ? base.type->wheel->diameter * base.type->wheel->width : 0;
483,456✔
347
}
348

349
/** Get wheel diameter (inches) or return 0 if part is not wheel */
350
int vehicle_part::wheel_diameter() const
302✔
351
{
352
    return base.is_wheel() ? base.type->wheel->diameter : 0;
334✔
353
}
354

355
/** Get wheel width (inches) or return 0 if part is not wheel */
356
int vehicle_part::wheel_width() const
×
357
{
358
    return base.is_wheel() ? base.type->wheel->width : 0;
×
359
}
360

361
npc *vehicle_part::crew() const
×
362
{
363
    if( is_broken() || crew_id < 0 ) {
×
364
        return nullptr;
365
    }
366

367
    npc *const res = g->critter_by_id<npc>( crew_id );
×
368
    if( !res ) {
×
369
        return nullptr;
370
    }
371
    return res->is_friend() ? res : nullptr;
×
372
}
373

374
bool vehicle_part::set_crew( const npc &who )
×
375
{
376
    if( who.is_dead_state() || !who.is_friend() ) {
×
377
        return false;
378
    }
379
    if( is_broken() || ( !is_seat() && !is_turret() ) ) {
×
380
        return false;
381
    }
382
    crew_id = who.getID();
×
383
    return true;
×
384
}
385

386
void vehicle_part::unset_crew()
×
387
{
388
    crew_id = -1;
×
389
}
390

391
void vehicle_part::reset_target( const tripoint &pos )
×
392
{
393
    target.first = pos;
×
394
    target.second = pos;
×
395
}
396

397
bool vehicle_part::is_engine() const
10,047,187✔
398
{
399
    return info().has_flag( VPFLAG_ENGINE );
20,094,374✔
400
}
401

402
bool vehicle_part::is_light() const
2✔
403
{
404
    const auto &vp = info();
2✔
405
    return vp.has_flag( VPFLAG_CONE_LIGHT ) ||
2✔
406
           vp.has_flag( VPFLAG_CIRCLE_LIGHT ) ||
2✔
407
           vp.has_flag( VPFLAG_AISLE_LIGHT ) ||
2✔
408
           vp.has_flag( VPFLAG_DOME_LIGHT ) ||
4✔
409
           vp.has_flag( VPFLAG_ATOMIC_LIGHT );
2✔
410
}
411

412
bool vehicle_part::is_fuel_store( bool skip_broke ) const
10,273,653✔
413
{
414
    if( skip_broke && is_broken() ) {
10,273,653✔
415
        return false;
416
    }
417
    return is_tank() || base.is_magazine() || is_reactor();
10,273,602✔
418
}
419

420
bool vehicle_part::is_tank() const
10,474,554✔
421
{
422
    return base.is_watertight_container();
31,378,965✔
423
}
424

425
bool vehicle_part::is_battery() const
10,476,213✔
426
{
427
    return base.is_magazine() && base.ammo_type() == "battery";
10,771,607✔
428
}
429

430
bool vehicle_part::is_reactor() const
10,196,236✔
431
{
432
    return info().has_flag( "REACTOR" );
10,196,236✔
433
}
434

435
bool vehicle_part::is_turret() const
×
436
{
437
    return base.is_gun();
10,174,676✔
438
}
439

440
bool vehicle_part::is_seat() const
×
441
{
442
    return info().has_flag( "SEAT" );
×
443
}
444

445
const vpart_info &vehicle_part::info() const
58,581,144✔
446
{
447
    if( !info_cache ) {
58,581,144✔
448
        info_cache = &id.obj();
8,375✔
449
    }
450
    return *info_cache;
58,581,144✔
451
}
452

453
void vehicle::set_hp( vehicle_part &pt, int qty )
6,225✔
454
{
455
    if( qty == pt.info().durability || pt.info().durability <= 0 ) {
6,225✔
456
        pt.base.set_damage( 0 );
5,840✔
457

458
    } else if( qty == 0 ) {
385✔
459
        pt.base.set_damage( pt.base.max_damage() );
25✔
460

461
    } else {
462
        pt.base.set_damage( pt.base.max_damage() - pt.base.max_damage() * qty / pt.info().durability );
360✔
463
    }
464
}
6,225✔
465

466
bool vehicle::mod_hp( vehicle_part &pt, int qty, damage_type dt )
236✔
467
{
468
    if( pt.info().durability > 0 ) {
236✔
469
        return pt.base.mod_damage( -( pt.base.max_damage() * qty / pt.info().durability ), dt );
236✔
470
    } else {
471
        return false;
472
    }
473
}
474

475
bool vehicle::can_enable( const vehicle_part &pt, bool alert ) const
×
476
{
477
    if( std::none_of( parts.begin(), parts.end(), [&pt]( const vehicle_part & e ) {
×
478
    return &e == &pt;
479
} ) || pt.removed ) {
×
480
        debugmsg( "Cannot enable removed or non-existent part" );
×
481
    }
482

483
    if( pt.is_broken() ) {
×
484
        return false;
485
    }
486

487
    if( pt.info().has_flag( "PLANTER" ) && !warm_enough_to_plant() ) {
×
488
        if( alert ) {
×
489
            add_msg( m_bad, _( "It is too cold to plant anything now." ) );
×
490
        }
491
        return false;
492
    }
493

494
    // @todo: check fuel for combustion engines
495

496
    if( pt.info().epower < 0 && fuel_left( fuel_type_battery, true ) <= 0 ) {
×
497
        if( alert ) {
×
498
            add_msg( m_bad, _( "Insufficient power to enable %s" ), pt.name() );
×
499
        }
500
        return false;
501
    }
502

503
    return true;
504
}
505

506
bool vehicle::assign_seat( vehicle_part &pt, const npc &who )
×
507
{
508
    if( !pt.is_seat() || !pt.set_crew( who ) ) {
×
509
        return false;
510
    }
511

512
    // NPC's can only be assigned to one seat in the vehicle
513
    for( auto &e : parts ) {
×
514
        if( &e == &pt ) {
×
515
            continue; // skip this part
516
        }
517

518
        if( e.is_seat() ) {
×
519
            const npc *n = e.crew();
×
520
            if( n && n->getID() == who.getID() ) {
×
521
                e.unset_crew();
522
            }
523
        }
524
    }
525

526
    return true;
527
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc