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

polserver / polserver / 25988041997

17 May 2026 10:13AM UTC coverage: 60.832% (-0.07%) from 60.903%
25988041997

Pull #884

github

turleypol
run hitscript, cleanup
Pull Request #884: Attackable item

268 of 559 new or added lines in 26 files covered. (47.94%)

18 existing lines in 7 files now uncovered.

44718 of 73511 relevant lines covered (60.83%)

502068.74 hits per line

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

47.11
/pol-core/pol/repsys.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/12/05 MuadDib:   Added ssopt.invul_tag for Repsys::hilite_color_idx
5
 * - 2005/12/05 MuadDib:   Added ssopt.invul_tag for NPC::hilite_color_idx
6
 * - 2005/12/05 MuadDib:   Added repsys.invulnerable color for names.
7
 * - 2009/08/06 Turley:    Added escript interface to add/remove aggressor/lawfullydamaged
8
 */
9

10

11
#include "repsys.h"
12

13
#include <stddef.h>
14
#include <string>
15

16
#include "../clib/cfgelem.h"
17
#include "../clib/cfgfile.h"
18
#include "../clib/cfgsect.h"
19
#include "../clib/logfacility.h"
20
#include "../clib/rawtypes.h"
21
#include "cmbtcfg.h"
22
#include "fnsearch.h"
23
#include "globals/settings.h"
24
#include "globals/state.h"
25
#include "guilds.h"
26
#include "mobile/charactr.h"
27
#include "mobile/npc.h"
28
#include "network/pktdef.h"
29
#include "npctmpl.h"
30
#include "party.h"
31
#include "polsig.h"
32
#include "repsys_cfg.h"
33
#include "schedule.h"
34
#include "syshook.h"
35
#include "ufunc.h"
36
#include "uworld.h"
37

38
// BUGS:
39
//  it looks like you can restart someone's aggressor timer by toggling war mode and setting
40
//  opponent.
41
//
42

43
namespace Pol
44
{
45
namespace Core
46
{
47
/// Reputation System
48

49
/// [1] Reputation System Configuration
50
/// Reputation System Configuration is read from config/repsys.cfg
51
/// This file is re-read on a config reload.
52
/// [1.1] General Configuration
53
///  Settings are defined in the General section:
54
///    CriminalFlagInterval    Time, in seconds, for which you will be marked criminal
55
///    AggressorFlagTimeout    Time, in seconds, for which you will be marked an aggressor
56
///
57
///
58
/// [1.2] Color Configuration
59
///  Name colors are defined in the NameColoring section:
60
///    Murderer
61
///    Criminal
62
///    Attackable
63
///    Innocent
64
///    GuildAlly
65
///    GuildEnemy
66
///    Invulnerable
67
///  The client specifies highlight colors.
68
///
69

70
void load_repsys_cfg_namecoloring( Clib::ConfigElem& elem )
3✔
71
{
72
  settingsManager.repsys_cfg.NameColoring.Murderer = elem.remove_ushort( "Murderer" );
3✔
73
  settingsManager.repsys_cfg.NameColoring.Criminal = elem.remove_ushort( "Criminal" );
3✔
74
  settingsManager.repsys_cfg.NameColoring.Attackable = elem.remove_ushort( "Attackable" );
3✔
75
  settingsManager.repsys_cfg.NameColoring.Innocent = elem.remove_ushort( "Innocent" );
3✔
76
  settingsManager.repsys_cfg.NameColoring.GuildAlly = elem.remove_ushort( "GuildAlly" );
3✔
77
  settingsManager.repsys_cfg.NameColoring.GuildEnemy = elem.remove_ushort( "GuildEnemy" );
3✔
78
  settingsManager.repsys_cfg.NameColoring.Invulnerable = elem.remove_ushort( "Invulnerable" );
3✔
79
}
3✔
80
void load_repsys_cfg_general( Clib::ConfigElem& elem )
3✔
81
{
82
  settingsManager.repsys_cfg.General.CriminalFlagInterval =
3✔
83
      elem.remove_ushort( "CriminalFlagInterval" );
3✔
84
  settingsManager.repsys_cfg.General.AggressorFlagTimeout =
3✔
85
      elem.remove_ushort( "AggressorFlagTimeout" );
3✔
86
  settingsManager.repsys_cfg.General.PartyHelpFullCountsAsCriminal =
3✔
87
      elem.remove_bool( "PartyHelpFullCountsAsCriminal", false );
3✔
88
  settingsManager.repsys_cfg.General.PartyHarmFullCountsAsCriminal =
3✔
89
      elem.remove_bool( "PartyHarmFullCountsAsCriminal", true );
3✔
90
}
3✔
91
void load_repsys_cfg_hooks( Clib::ConfigElem& elem )
×
92
{
93
  std::string temp;
×
94
  if ( elem.remove_prop( "NameColor", &temp ) )
×
95
    settingsManager.repsys_cfg.Hooks.NameColor = FindExportedFunction( elem, nullptr, temp, 2 );
×
96
  if ( elem.remove_prop( "HighLightColor", &temp ) )
×
97
    settingsManager.repsys_cfg.Hooks.HighLightColor =
×
98
        FindExportedFunction( elem, nullptr, temp, 2 );
×
99

100
  if ( elem.remove_prop( "OnAttack", &temp ) )
×
101
    settingsManager.repsys_cfg.Hooks.OnAttack = FindExportedFunction( elem, nullptr, temp, 2 );
×
102
  if ( elem.remove_prop( "OnDamage", &temp ) )
×
103
    settingsManager.repsys_cfg.Hooks.OnDamage = FindExportedFunction( elem, nullptr, temp, 2 );
×
104
  if ( elem.remove_prop( "OnHelp", &temp ) )
×
105
    settingsManager.repsys_cfg.Hooks.OnHelp = FindExportedFunction( elem, nullptr, temp, 2 );
×
106
}
×
107

108
void unload_repsys_cfg_hooks()
3✔
109
{
110
  if ( settingsManager.repsys_cfg.Hooks.NameColor != nullptr )
3✔
111
  {
112
    delete settingsManager.repsys_cfg.Hooks.NameColor;
×
113
    settingsManager.repsys_cfg.Hooks.NameColor = nullptr;
×
114
  }
115
  if ( settingsManager.repsys_cfg.Hooks.HighLightColor != nullptr )
3✔
116
  {
117
    delete settingsManager.repsys_cfg.Hooks.HighLightColor;
×
118
    settingsManager.repsys_cfg.Hooks.HighLightColor = nullptr;
×
119
  }
120
  if ( settingsManager.repsys_cfg.Hooks.OnAttack != nullptr )
3✔
121
  {
122
    delete settingsManager.repsys_cfg.Hooks.OnAttack;
×
123
    settingsManager.repsys_cfg.Hooks.OnAttack = nullptr;
×
124
  }
125
  if ( settingsManager.repsys_cfg.Hooks.OnDamage != nullptr )
3✔
126
  {
127
    delete settingsManager.repsys_cfg.Hooks.OnDamage;
×
128
    settingsManager.repsys_cfg.Hooks.OnDamage = nullptr;
×
129
  }
130
  if ( settingsManager.repsys_cfg.Hooks.OnHelp != nullptr )
3✔
131
  {
132
    delete settingsManager.repsys_cfg.Hooks.OnHelp;
×
133
    settingsManager.repsys_cfg.Hooks.OnHelp = nullptr;
×
134
  }
135
}
3✔
136

137
void unload_repsys_cfg()
3✔
138
{
139
  unload_repsys_cfg_hooks();
3✔
140
}
3✔
141

142
void load_repsys_cfg( bool reload )
3✔
143
{
144
  Clib::ConfigFile cf( "config/repsys.cfg" );
3✔
145

146
  Clib::ConfigSection namecoloring( cf, "NameColoring" );
3✔
147
  Clib::ConfigSection general( cf, "General" );
3✔
148
  Clib::ConfigSection hooks( cf, "HookList", Clib::CST_NORMAL );
3✔
149

150
  Clib::ConfigElem elem;
3✔
151

152
  while ( cf.read( elem ) )
9✔
153
  {
154
    if ( namecoloring.matches( elem ) )
6✔
155
    {
156
      load_repsys_cfg_namecoloring( elem );
3✔
157
    }
158
    else if ( general.matches( elem ) )
3✔
159
    {
160
      load_repsys_cfg_general( elem );
3✔
161
    }
162
    else if ( hooks.matches( elem ) && !reload )
×
163
    {
164
      load_repsys_cfg_hooks( elem );
×
165
    }
166
  }
167
}
3✔
168

169
///
170
/// [2] Timeouts
171
/// When Any of Amy's Rep-system timers time out
172
///
173
/// If Amy's Criminal Timer has expired,
174
///   Clear Amy's Criminal Timer
175
///
176
/// If Amy was Aggressor to Bob, but has timed out
177
///   De-escalate[->7] Amy and Bob
178
///   Update Bob to nearby clients
179
///
180
/// If Amy had Lawfully Damaged Bob, but has timed out
181
///   De-escalate[->7] Amy and Bob
182
///   Update Bob to nearby clients
183
///
184
/// If any of Amy's statuses changed,
185
///   Update Amy to nearby clients
186

187
void RepSystem::repsys_task( Mobile::Character* amy )
1✔
188
{
189
  THREAD_CHECKPOINT( tasks, 1000 );
1✔
190
  polclock_t now = polclock();
1✔
191
  bool refreshall = false;
1✔
192

193
  // the 'defensive hostiles' are those that are attacking Amy while she's not innocent.
194
  // If she's innocent at the end of this, they will break off the attack.
195
  THREAD_CHECKPOINT( tasks, 1001 );
1✔
196
  std::set<Mobile::Character*> defensive_hostiles;
1✔
197
  for ( const auto& opp : amy->opponent_of )
1✔
198
  {
NEW
199
    if ( auto* bob = opp.mobile() )
×
200
    {
NEW
201
      THREAD_CHECKPOINT( tasks, 1002 );
×
202

NEW
203
      if ( !amy->is_innocent_to( bob ) )
×
204
      {
NEW
205
        THREAD_CHECKPOINT( tasks, 1003 );
×
NEW
206
        defensive_hostiles.insert( bob );
×
207
      }
NEW
208
      THREAD_CHECKPOINT( tasks, 1004 );
×
209
    }
210
  }
211

212

213
  THREAD_CHECKPOINT( tasks, 1005 );
1✔
214
  if ( amy->criminal_until_ && timer_expired( amy->criminal_until_, now ) )
1✔
215
  {
216
    amy->criminal_until_ = 0;
×
217
    refreshall = true;
×
218
  }
219

220
  THREAD_CHECKPOINT( tasks, 1006 );
1✔
221
  polclock_t earliest_timeout;
222

223
  if ( amy->criminal_until_ )
1✔
224
    earliest_timeout = amy->criminal_until_;
×
225
  else
226
    earliest_timeout = now + 5 * 60 * POLCLOCKS_PER_SEC;  // 5 minutes, later code will trim
1✔
227

228
  THREAD_CHECKPOINT( tasks, 1007 );
1✔
229

230
  Mobile::Character::MobileCont::iterator itr;
1✔
231
  itr = amy->aggressor_to_.begin();
1✔
232
  while ( itr != amy->aggressor_to_.end() )
1✔
233
  {
234
    THREAD_CHECKPOINT( tasks, 1008 );
×
235
    Mobile::Character::MobileCont::iterator save_itr = itr;
×
236
    ++itr;
×
237
    polclock_t when = ( *save_itr ).second;
×
238
    THREAD_CHECKPOINT( tasks, 1009 );
×
239
    if ( timer_expired( when, now ) )
×
240
    {
241
      THREAD_CHECKPOINT( tasks, 1010 );
×
242
      CharacterRef other = ( *save_itr ).first;
×
243
      // not sure if this is right.  what if also lawfully_damaged ?
244
      THREAD_CHECKPOINT( tasks, 1011 );
×
245
      de_escalate( amy, other.get() );
×
246
      THREAD_CHECKPOINT( tasks, 1012 );
×
247
      amy->aggressor_to_.erase( save_itr );
×
248
      THREAD_CHECKPOINT( tasks, 1013 );
×
249
      if ( !other->orphan() )
×
250
      {
251
        // Changed to update!
252
        if ( amy->client )
×
253
          send_move( amy->client, amy );
×
254
        send_create_mobile_to_nearby_cansee( other.get() );
×
255
      }
256
      else
257
        POLLOGLN( "NOTE: repsys crash avoidance..." );
×
258
      THREAD_CHECKPOINT( tasks, 1014 );
×
259
      refreshall = true;
×
260
    }
×
261
    else
262
    {
263
      earliest_timeout = earliest_timer( when, earliest_timeout );
×
264
    }
265
    THREAD_CHECKPOINT( tasks, 1015 );
×
266
  }
267

268
  THREAD_CHECKPOINT( tasks, 1016 );
1✔
269
  itr = amy->lawfully_damaged_.begin();
1✔
270
  while ( itr != amy->lawfully_damaged_.end() )
1✔
271
  {
272
    THREAD_CHECKPOINT( tasks, 1017 );
×
273
    Mobile::Character::MobileCont::iterator save_itr = itr;
×
274
    ++itr;
×
275
    polclock_t when = ( *save_itr ).second;
×
276
    THREAD_CHECKPOINT( tasks, 1018 );
×
277
    if ( timer_expired( when, now ) )
×
278
    {
279
      THREAD_CHECKPOINT( tasks, 1019 );
×
280
      CharacterRef other = ( *save_itr ).first;
×
281
      // not sure if this is right.  what if also aggressor_to ?
282
      THREAD_CHECKPOINT( tasks, 1020 );
×
283
      de_escalate( amy, other.get() );
×
284
      THREAD_CHECKPOINT( tasks, 1021 );
×
285
      amy->lawfully_damaged_.erase( save_itr );
×
286
      THREAD_CHECKPOINT( tasks, 1022 );
×
287
      if ( !other->orphan() )
×
288
      {
289
        // Changed to update!
290
        if ( amy->client )
×
291
          send_move( amy->client, amy );
×
292
        send_create_mobile_to_nearby_cansee( other.get() );
×
293
      }
294
      else
295
        POLLOGLN( "NOTE: repsys crash avoidance..." );
×
296
      THREAD_CHECKPOINT( tasks, 1023 );
×
297
      refreshall = true;
×
298
    }
×
299
    else
300
    {
301
      earliest_timeout = earliest_timer( when, earliest_timeout );
×
302
    }
303
    THREAD_CHECKPOINT( tasks, 1024 );
×
304
  }
305

306
  THREAD_CHECKPOINT( tasks, 1025 );
1✔
307
  // For those that were hostile to Amy due to her non-Innocence, if she's Innocent now,
308
  // break off the attack.
309
  for ( auto* bob : defensive_hostiles )
1✔
310
  {
311
    THREAD_CHECKPOINT( tasks, 1026 );
×
312

313
    if ( amy->is_innocent_to( bob ) )
×
314
    {
315
      THREAD_CHECKPOINT( tasks, 1027 );
×
316
      de_escalate( amy, bob );
×
317
      THREAD_CHECKPOINT( tasks, 1028 );
×
318

319
      refreshall = true;
×
320
    }
321
  }
322

323
  THREAD_CHECKPOINT( tasks, 1029 );
1✔
324

325
  if ( amy->criminal_until_ || !amy->aggressor_to_.empty() || !amy->lawfully_damaged_.empty() )
1✔
326
  {
327
    THREAD_CHECKPOINT( tasks, 1030 );
×
328
    schedule_repsys_task( amy, earliest_timeout + 1 );
×
329
  }
330

331
  THREAD_CHECKPOINT( tasks, 1031 );
1✔
332
  if ( refreshall )
1✔
333
  {
334
    THREAD_CHECKPOINT( tasks, 1032 );
×
335
    if ( !amy->orphan() )
×
336
    {
337
      // Changed to update!
338
      if ( amy->client )
×
339
        send_move( amy->client, amy );
×
340
      send_create_mobile_to_nearby_cansee( amy );
×
341
    }
342
  }
343
  THREAD_CHECKPOINT( tasks, 1099 );
1✔
344
}
1✔
345

346

347
void RepSystem::schedule_repsys_task( Mobile::Character* chr, polclock_t runat )
3✔
348
{
349
  if ( chr->repsys_task_ != nullptr )
3✔
350
  {
351
    if ( chr->repsys_task_->next_run_clock() > runat )
×
352
      chr->repsys_task_->cancel();
×
353
  }
354

355
  if ( chr->repsys_task_ == nullptr )
3✔
356
  {
357
    new OneShotTaskInst<Mobile::Character*>( &chr->repsys_task_, runat, RepSystem::repsys_task,
3✔
358
                                             chr );
3✔
359
  }
360
}
3✔
361
bool private_say_above_ex( Mobile::Character* chr, const UObject* obj, const char* text,
362
                           unsigned short color );
363
// bool RepSystem::can_attack( Character* attacker, Character* defender )
364
// {
365
//   return (defender->is_aggressor_to( attacker ) ||
366
//     attacker->has_lawfully_damaged(defender) ||
367
//     defender->is_criminal());
368
// }
369
//
370

371
// a scenario:
372
// A (innocent) attacks B (innocent):
373
//   (3), so A criminal, aggressor to B
374
// A (crim,agg) attacks B (innocent):
375
//   (1), so A's aggressor timer restarts (not criminal timer)
376
// B (innocent) attacks A (crim,agg):
377
//   (2b), so lawfully damaged timer restarts
378
// B (innocent) attacks A (crim,agg):
379
//   (2a), so lawfully damaged timer restarts
380
// A (crim, agg) attacks B (innocent):
381
//   umm, what should happen?
382

383

384
///
385
/// [3] Player attacks another Player
386
/// When Amy attacks Bob
387
///   These rules are applied:
388
///   - When Amy applies damage to Bob (on_pc_damaged_pc calls this)
389
///   - repsys_on_attack (Amy attacks PC, or tamed creature)
390
///     - apply_raw_damage_hundredths
391
///     - amy sets opponent
392
///     - melee attack
393
///     - amy's NPC does any of these things
394
///   - When Amy tries to apply any damage to Bob
395
///   - When Amy selects Bob as her Opponent
396
///   - When Amy swings at Bob
397
///   - When Amy selects Bob with a Harmful target cursor
398
///
399
///   if Bob is Innocent to Amy,
400
///     Set Amy's Criminal flag for CriminalFlagInterval seconds
401
///
402
///   if Bob is Aggressor to Amy,
403
///     Set Bob as Aggressor to Amy for AggressorFlagTimeout seconds
404
///   else,
405
///     Tell Bob "Amy is Attacking you!" if this is news
406
///     Set Amy as Aggressor to Bob for AggressorFlagTimeout seconds
407
///
408

409
void RepSystem::on_pc_attacks_pc( Mobile::Character* amy_attacker, Mobile::Character* bob_defender )
2✔
410
{
411
  if ( amy_attacker == bob_defender )
2✔
412
    return;
1✔
413

414
  bool refresh = false;
1✔
415

416
  polclock_t now = polclock();
1✔
417
  polclock_t crim_timeout_at =
1✔
418
      now + settingsManager.repsys_cfg.General.CriminalFlagInterval * POLCLOCKS_PER_SEC;
1✔
419
  polclock_t aggr_timeout_at =
1✔
420
      now + settingsManager.repsys_cfg.General.AggressorFlagTimeout * POLCLOCKS_PER_SEC;
1✔
421

422
  if ( bob_defender->is_innocent_to( amy_attacker ) )
1✔
423
  {
424
    if ( !amy_attacker->is_criminal() )
1✔
425
      refresh = true;
1✔
426
    amy_attacker->restart_criminal_timer( crim_timeout_at );
1✔
427
  }
428

429
  if ( bob_defender->is_aggressor_to( amy_attacker ) )
1✔
430
  {
431
    bob_defender->restart_aggressor_timer( amy_attacker, aggr_timeout_at );
×
432
  }
433
  else  // he's not the aggressor, so I guess I am.
434
  {
435
    if ( !amy_attacker->is_aggressor_to( bob_defender ) )
1✔
436
    {
437
      if ( settingsManager.combat_config.send_attack_msg )
1✔
438
      {
439
        std::string msg = "*" + amy_attacker->name() + " is attacking you!*";
1✔
440
        private_say_above_ex( bob_defender, bob_defender, msg.c_str(),
1✔
441
                              settingsManager.repsys_cfg.NameColoring.Murderer );
1✔
442
      }
1✔
443
      refresh = true;
1✔
444
    }
445
    amy_attacker->restart_aggressor_timer( bob_defender, aggr_timeout_at );
1✔
446
  }
447

448
  if ( refresh )
1✔
449
  {
450
    // Changed to update!
451
    if ( amy_attacker->client )
1✔
452
      send_move( amy_attacker->client, amy_attacker );
×
453
    send_create_mobile_to_nearby_cansee( amy_attacker );
1✔
454
  }
455
  polclock_t timeout_at = earliest_timer( crim_timeout_at, aggr_timeout_at );
1✔
456

457
  schedule_repsys_task( amy_attacker, timeout_at + 1 );
1✔
458
  schedule_repsys_task( bob_defender, timeout_at + 1 );
1✔
459
}
460

461
///
462
/// [4] Player Damages Player
463
/// When Amy damages Bob
464
///   These rules applied:
465
///   - When Amy actually applies damage to Bob
466
///   - When Amy poisons Bob (only with Bob.SetPoisoned(1))
467
///     (note damage due to poisoning will not fire this rule,
468
///     only the initial application)
469
///   - When Amy paralyzes Bob (only with Bob.SetParalyzed(1))
470
///
471
///   Apply all rules for "Player Attacks Another Player"[->3]
472
///
473
///   If Bob is Innocent to Amy,
474
///     Add Amy to Bob's "ToBeReportable" list
475
///   else
476
///     Set Amy as having LawfullyDamaged Bob for AggressorFlagTimeout seconds
477
///
478
///
479
///
480
void RepSystem::on_pc_damages_pc( Mobile::Character* amy, Mobile::Character* bob )
1✔
481
{
482
  if ( amy == bob )
1✔
483
    return;
×
484
  polclock_t aggr_timeout_at =
485
      polclock() + settingsManager.repsys_cfg.General.AggressorFlagTimeout * POLCLOCKS_PER_SEC;
1✔
486

487
  on_pc_attacks_pc( amy, bob );
1✔
488

489
  if ( bob->is_innocent_to( amy ) )
1✔
490
  {
491
    bob->add_to_be_reportable( amy->serial );
1✔
492
  }
493
  else
494
  {
495
    amy->restart_lawfully_damaged_timer( bob, aggr_timeout_at );
×
496
    schedule_repsys_task( amy, aggr_timeout_at + 1 );
×
497
  }
498
}
499

500

501
///
502
/// [5] Player Helps Another Player
503
/// When Amy Helps Bob
504
///   These rules applied:
505
///   - When Amy selects Bob with a Helpful target cursor
506
///   - When Amy Heals Bob
507
///   - When Amy clear's Bob's Poisoned flag
508
///   - When Amy clear's Bob's Paralyzed flag
509
///
510
/// If PartyHelpFullCountsAsCriminal false and same party dont set
511
///  If Bob is a Criminal, Set Amy Criminal
512
///
513

514
void RepSystem::on_pc_helps_pc( Mobile::Character* amy, Mobile::Character* bob )
×
515
{
516
  if ( ( amy != bob ) && bob->is_criminal() )
×
517
  {
518
    if ( !settingsManager.repsys_cfg.General.PartyHelpFullCountsAsCriminal )
×
519
    {
520
      Party* party = amy->party();
×
521
      if ( ( party != nullptr ) && ( party->is_member( bob->serial ) ) )
×
522
        return;
×
523
    }
524
    amy->make_criminal();
×
525
  }
526
}
527

528
/// [7] De-Escalation
529
/// To De-escalate Amy and Bob:
530
///   if Amy's opponent is Bob, Amy clears her opponent
531
///   If Bob's opponent is Amy, Bob clears his opponent
532
///
533

534
void RepSystem::de_escalate( Mobile::Character* amy, Mobile::Character* bob )
×
535
{
NEW
536
  if ( amy->opponent_.object() == bob )
×
537
  {
NEW
538
    amy->set_opponent( {}, true );
×
539
  }
540

NEW
541
  if ( bob->opponent_.object() == amy )
×
542
  {
NEW
543
    bob->set_opponent( {}, true );
×
544
  }
545
}
×
546

547
///
548
/// [8] Highlighting and Name Coloring
549
///   if Amy looks at Bob, coloring is as follows:
550
///     if Bob is invul and invul tag set 2, color Bob INVUL (yellow)
551
///     if Bob is a Murderer                 color Bob MURDERER ()
552
///     if Bob is a Criminal,                color Bob CRIMINAL (Red)
553
///     If Bob is a guild ally of Amy's,     color Bob FRIEND (Green)
554
///     if Bob is an Aggressor to Amy,       color Bob ATTACKABLE (Grey)
555
///     if Amy has lawfully damaged Bob,     color Bob ATTACKABLE (Grey)
556
///     if Bob is a guild enemy of Amy's,    color Bob ENEMY (Orange)
557
///     otherwise                            color Bob INNOCENT (Blue)
558
///
559

560
unsigned char RepSystem::hilite_color_idx( const Mobile::Character* amy,
102✔
561
                                           const Mobile::Character* bob )
562
{
563
  if ( settingsManager.repsys_cfg.Hooks.HighLightColor )
102✔
564
  {
565
    Mobile::Character* t_amy = const_cast<Mobile::Character*>( amy );
×
566
    Mobile::Character* t_bob = const_cast<Mobile::Character*>( bob );
×
567
    int hook_highlight = settingsManager.repsys_cfg.Hooks.HighLightColor->call_long(
×
568
        t_bob->make_ref(), t_amy->make_ref() );
×
569
    if ( hook_highlight != -1 )
×
570
      return (unsigned char)hook_highlight;
×
571
  }
572

573
  if ( ( settingsManager.ssopt.invul_tag == 2 ) && bob->invul() )
102✔
574
    return CHAR_HILITE_INVUL;
×
575
  if ( bob->is_murderer() )
102✔
576
    return CHAR_HILITE_MURDERER;
×
577
  if ( bob->is_criminal() )
102✔
578
    return CHAR_HILITE_CRIMINAL;
×
579
  if ( bob->is_guild_ally( amy ) )
102✔
580
    return CHAR_HILITE_FRIEND;
×
581
  if ( bob->is_aggressor_to( amy ) || amy->has_lawfully_damaged( bob ) )
102✔
582
    return CHAR_HILITE_ATTACKABLE;
×
583
  if ( bob->is_guild_enemy( amy ) )
102✔
584
    return CHAR_HILITE_ENEMY;
×
585

586
  return CHAR_HILITE_INNOCENT;
102✔
587
}
588

589
/*
590
 * AMY = Mobile doing the seeing
591
 * BOB = Mobile being seen.
592
 *
593
 */
594
unsigned short RepSystem::name_color( const Mobile::Character* amy, const Mobile::Character* bob )
30✔
595
{
596
  if ( settingsManager.repsys_cfg.Hooks.NameColor )
30✔
597
  {
598
    Mobile::Character* t_amy = const_cast<Mobile::Character*>( amy );
×
599
    Mobile::Character* t_bob = const_cast<Mobile::Character*>( bob );
×
600
    int hook_color = settingsManager.repsys_cfg.Hooks.NameColor->call_long( t_bob->make_ref(),
×
601
                                                                            t_amy->make_ref() );
×
602
    if ( hook_color != -1 )
×
603
      return (unsigned short)hook_color;
×
604
  }
605

606
  if ( settingsManager.ssopt.invul_tag == 2 )
30✔
607
  {
608
    if ( bob->invul() )
×
609
      return settingsManager.repsys_cfg.NameColoring.Invulnerable;
×
610
  }
611
  if ( bob->is_murderer() )
30✔
612
    return settingsManager.repsys_cfg.NameColoring.Murderer;
×
613
  if ( bob->is_criminal() )
30✔
614
    return settingsManager.repsys_cfg.NameColoring.Criminal;
×
615
  if ( bob->is_guild_ally( amy ) )
30✔
616
    return settingsManager.repsys_cfg.NameColoring.GuildAlly;
×
617
  if ( bob->is_aggressor_to( amy ) || amy->has_lawfully_damaged( bob ) )
30✔
618
    return settingsManager.repsys_cfg.NameColoring.Attackable;
×
619
  if ( bob->is_guild_enemy( amy ) )
30✔
620
    return settingsManager.repsys_cfg.NameColoring.GuildEnemy;
×
621
  return settingsManager.repsys_cfg.NameColoring.Innocent;
30✔
622
}
623
}  // namespace Core
624
namespace Mobile
625
{
626
///
627
/// [9] Innocent Status
628
/// Bob is Innocent to Amy if:
629
/// ===============================
630
/// Bob is Innocent to Amy only if NONE of the following are true:
631
///   Bob is a murderer;
632
///   Bob is a criminal
633
///   Bob is an Aggressor to Amy
634
///   Bob is a Guild Ally of Amy
635
///   Bob is a Guild Enemy of Amy
636
///   Amy has Lawfully Damaged Bob
637
///   If PartyHarmFullCountsAsCriminal true and Bob is in same Party as Amy
638
///
639
bool Character::is_innocent_to( const Character* amy ) const
2✔
640
{
641
  // this == bob
642
  const Character& bob = *this;
2✔
643

644
  if ( bob.is_murderer() )
2✔
645
    return false;
×
646

647
  if ( bob.is_criminal() )
2✔
648
    return false;
×
649

650
  if ( bob.is_aggressor_to( amy ) )
2✔
651
    return false;
×
652

653
  if ( bob.is_guild_ally( amy ) )
2✔
654
    return false;
×
655

656
  if ( bob.is_guild_enemy( amy ) )
2✔
657
    return false;
×
658

659
  if ( amy->has_lawfully_damaged( &bob ) )
2✔
660
    return false;
×
661

662
  if ( !Core::settingsManager.repsys_cfg.General.PartyHarmFullCountsAsCriminal )
2✔
663
  {
664
    Core::Party* party = amy->party();
×
665
    if ( ( party != nullptr ) && ( party->is_member( bob.serial ) ) )
×
666
      return false;
×
667
  }
668

669
  return true;
2✔
670
}
671

672
bool Character::is_aggressor_to( const Character* chr ) const
136✔
673
{
674
  Character* in_chr = const_cast<Character*>( chr );
136✔
675
  return ( aggressor_to_.find( Core::CharacterRef( in_chr ) ) != aggressor_to_.end() );
136✔
676
}
677

678
///
679
/// [10] Guild Rules
680
/// The following Guild Rules are only temporary pending a more complete system
681
///
682
/// [10.1] Guild Allies
683
/// Bob and Amy are Guild Allies if:
684
///   Bob is in a guild, AND
685
///   Amy is in a guild, AND
686
///   Bob's Guild is allied with Amy's guild
687
///
688
bool Character::is_guild_ally( const Character* chr ) const
134✔
689
{
690
  auto thisguild = guild();
134✔
691
  auto otherguild = chr->guild();
134✔
692
  return ( thisguild != nullptr && otherguild != nullptr &&
134✔
693
           Core::Guild::AreAllies( thisguild, otherguild ) );
134✔
694
}
695

696
///
697
/// [10.2] Guild Enemies
698
/// Bob and Amy are Guild Enemies if:
699
///   Bob is in a guild, AND
700
///   Amy is in a guild, AND
701
///   Bob's Guild is an Enemy Guild of Amy's Guild
702
///
703
///
704
bool Character::is_guild_enemy( const Character* chr ) const
134✔
705
{
706
  auto thisguild = guild();
134✔
707
  auto otherguild = chr->guild();
134✔
708
  return ( thisguild != nullptr && otherguild != nullptr &&
134✔
709
           Core::Guild::AreEnemies( thisguild, otherguild ) );
134✔
710
}
711

712
//
713
// To Mark Bob an Aggressor to Amy:
714
//   Bob is reported as an Aggressor to Amy for 2 minutes
715
//
716
//
717
void Character::restart_aggressor_timer( Character* amy, Core::polclock_t until )
1✔
718
{
719
  MobileCont::iterator itr = aggressor_to_.find( Core::CharacterRef( amy ) );
1✔
720
  if ( itr == aggressor_to_.end() )
1✔
721
  {
722
    aggressor_to_[Core::CharacterRef( amy )] = until;
1✔
723
  }
724
  else
725
  {
726
    if ( until > ( *itr ).second )
×
727
      ( *itr ).second = until;
×
728
  }
729
}
1✔
730

731
bool Character::has_lawfully_damaged( const Character* chr ) const
134✔
732
{
733
  Character* in_chr = const_cast<Character*>( chr );
134✔
734
  return ( lawfully_damaged_.find( Core::CharacterRef( in_chr ) ) != lawfully_damaged_.end() );
134✔
735
}
736

737
void Character::restart_lawfully_damaged_timer( Mobile::Character* amy, Core::polclock_t when )
×
738
{
739
  MobileCont::iterator itr = lawfully_damaged_.find( Core::CharacterRef( amy ) );
×
740
  if ( itr == lawfully_damaged_.end() )
×
741
  {
742
    lawfully_damaged_[Core::CharacterRef( amy )] = when;
×
743
  }
744
  else
745
  {
746
    if ( when > ( *itr ).second )
×
747
      ( *itr ).second = when;
×
748
  }
749
}
×
750

751
///
752
/// A Mobile is Criminal if:
753
///   he has an active Criminal Timer, which has not timed out.
754
///     OR
755
///   he is a murderer.
756
///
757
bool Character::is_criminal() const
135✔
758
{
759
  return is_temporally_criminal() || is_murderer();
135✔
760
}
761
bool Character::is_temporally_criminal() const
135✔
762
{
763
  return ( Core::polclock() <= criminal_until_ );
135✔
764
}
765

766
void Character::restart_criminal_timer( Core::polclock_t until )
1✔
767
{
768
  if ( until > criminal_until_ )
1✔
769
  {
770
    if ( criminal_until_ < Core::polclock() )
1✔
771
    {
772
      // TODO Pos see realm.cpp
773
      Core::WorldIterator<Core::NPCFilter>::InRange(
1✔
774
          this, 32, [&]( Character* chr ) { NpcPropagateCriminal( chr, this ); } );
1✔
775
    }
776
    criminal_until_ = until;
1✔
777
  }
778
}
1✔
779

780
void Character::clear_criminal_timer()
×
781
{
782
  criminal_until_ = 0;
×
783
}
×
784

785
bool Character::is_murderer() const
295✔
786
{
787
  return mob_flags_.get( MOB_FLAGS::MURDERER );
295✔
788
}
789

790

791
//
792
// Everything below this point is DOUBLE-DISPATCH for
793
//  PC vs NPC, NPC vs PC, NPC vs NPC
794
//  as well as PC -> NPC reactions.
795
//
796

797
/* the reputation system only comes into play if:
798
      a player attacks a player
799
      a player attacks a player's creature
800
      a player's creature attacks a player
801
      a player's creature attacks a player's creature
802
      */
803

804

805
///
806
/// [13] Mobile (MA) Attacks Mobile (MA)
807
///
808
///   Rules are applied based on whether MA is a PC or an NPC.
809
///
810
/// [13.1] Player (Amy) Attacks Mobile (Mob)
811
///   If Mob is a PC (Bob),
812
///     Apply 'Player Attacks Player' rules[->3] for Amy vs Bob.
813
///   Else if Mob is an NPC with a Master (Bob),
814
///     Apply 'Player Attacks Player' rules for Amy vs Bob
815
///   Else if Mob is a Good-aligned NPC,
816
///     Set Amy's Criminal Flag
817
///
818
void Character::repsys_on_attack( Character* defender )
1✔
819
{
820
  if ( Core::settingsManager.repsys_cfg.Hooks.OnAttack )
1✔
821
  {
822
    if ( Core::settingsManager.repsys_cfg.Hooks.OnAttack->call( this->make_ref(),
×
823
                                                                defender->make_ref() ) )
×
824
      return;
×
825
  }
826

827
  // consider double-dispatch here.
828
  if ( defender->isa( Core::UOBJ_CLASS::CLASS_CHARACTER ) )
1✔
829
  {
830
    Core::RepSystem::on_pc_attacks_pc( this, defender );
1✔
831
  }
832
  else
833
  {
834
    NPC* npc = static_cast<NPC*>( defender );
×
835
    if ( npc->master() )
×
836
    {
837
      Core::RepSystem::on_pc_attacks_pc( this, npc->master() );
×
838
    }
839
    else if ( ( npc->alignment() == Core::NpcTemplate::GOOD ) && ( !npc->is_criminal() ) )
×
840
    {
841
      make_criminal();
×
842
    }
843
  }
844
}
845

846
///
847
/// [13.2] NPC (MA) Attacks Mobile (MB)
848
///   If MA has a Master (Amy),
849
///     Apply 'Player Attacks Mobile' rules[->13.1] for Amy vs MB
850
///   Else
851
///     Exit with no (RepSystem) effect.
852
///
853
void NPC::repsys_on_attack( Character* defender )
12✔
854
{
855
  if ( Core::settingsManager.repsys_cfg.Hooks.OnAttack )
12✔
856
  {
857
    if ( Core::settingsManager.repsys_cfg.Hooks.OnAttack->call( this->make_ref(),
×
858
                                                                defender->make_ref() ) )
×
859
      return;
×
860
  }
861

862
  if ( master() )
12✔
863
  {
864
    master_->Character::repsys_on_attack( defender );
×
865
  }
866
}
867

868
///
869
/// [14] Mobile (MA) Damages Mobile (MB)
870
///
871
///   Rules are applied based on whether MA is a PC or an NPC.
872
///
873
/// [14.1] Player (Amy) Damages Mobile (Mob)
874
///   If Mob is a PC (Bob),
875
///     Apply 'Player Damages Player' rules[->4] for Amy vs Bob.
876
///   Else if Mob is an NPC with a Master (Bob),
877
///     Apply 'Player Damages Player' rules for Amy vs Bob
878
///   Else if Mob is a Good-aligned NPC,
879
///     Set Amy's Criminal Flag
880
///
881
void Character::repsys_on_damage( Character* defender )
1✔
882
{
883
  if ( Core::settingsManager.repsys_cfg.Hooks.OnDamage )
1✔
884
  {
885
    if ( Core::settingsManager.repsys_cfg.Hooks.OnDamage->call( this->make_ref(),
×
886
                                                                defender->make_ref() ) )
×
887
      return;
×
888
  }
889

890
  if ( defender->isa( Core::UOBJ_CLASS::CLASS_CHARACTER ) )
1✔
891
  {
892
    Core::RepSystem::on_pc_damages_pc( this, defender );
1✔
893
  }
894
  else
895
  {
896
    NPC* npc = static_cast<NPC*>( defender );
×
897
    if ( npc->master() )
×
898
    {
899
      Core::RepSystem::on_pc_damages_pc( this, npc->master() );
×
900
    }
901
    else if ( ( npc->alignment() == Core::NpcTemplate::GOOD ) && ( !npc->is_criminal() ) )
×
902
    {
903
      make_criminal();
×
904
    }
905
  }
906
}
907

908
///
909
/// [14.2] NPC (MA) Damages Mobile (MB)
910
///   If MA has a Master (Amy),
911
///     Apply 'Player Damages Mobile' rules[->14.1] for Amy vs MB
912
///   Else
913
///     Exit with no (RepSystem) effect.
914
///
915
void NPC::repsys_on_damage( Character* defender )
8✔
916
{
917
  if ( Core::settingsManager.repsys_cfg.Hooks.OnDamage )
8✔
918
  {
919
    if ( Core::settingsManager.repsys_cfg.Hooks.OnDamage->call( this->make_ref(),
×
920
                                                                defender->make_ref() ) )
×
921
      return;
×
922
  }
923

924
  if ( master() )
8✔
925
    master_->Character::repsys_on_damage( defender );
×
926
}
927

928
///
929
/// [15] Mobile (MA) Helps Mobile (MB)
930
///
931
///   Rules are applied based on whether MA is a PC or an NPC.
932
///
933
///   [15.1] Player (Amy) Helps Mobile (Mob)
934
///   If Mob is a PC (Bob),
935
///     Apply 'Player Helps Player' rules[->5] for Amy helps Bob.
936
///   Else if Mob is an NPC with a Master (Bob),
937
///     Apply 'Player Helps Player' rules for Amy helps Bob
938
///   Else if Mob is an Evil-aligned NPC,
939
///     Set Amy's Criminal Flag
940

941
void Character::repsys_on_help( Character* helped )
×
942
{
943
  if ( Core::settingsManager.repsys_cfg.Hooks.OnHelp )
×
944
  {
945
    if ( Core::settingsManager.repsys_cfg.Hooks.OnHelp->call( this->make_ref(),
×
946
                                                              helped->make_ref() ) )
×
947
      return;
×
948
  }
949

950
  // repsys_on_pc_helps( helped );
951
  if ( helped->isa( Core::UOBJ_CLASS::CLASS_CHARACTER ) )
×
952
  {
953
    Core::RepSystem::on_pc_helps_pc( this, helped );
×
954
  }
955
  else
956
  {
957
    NPC* npc = static_cast<NPC*>( helped );
×
958
    if ( npc->master() )
×
959
    {
960
      Core::RepSystem::on_pc_helps_pc( this, npc->master() );
×
961
    }
962
    else if ( npc->alignment() == Core::NpcTemplate::EVIL )
×
963
    {
964
      make_criminal();
×
965
    }
966
  }
967
}
968

969
///
970
///   [15.2] NPC (MA) Helps Mobile (MB)
971
///     If MA has a Master (Amy),
972
///       Apply 'Player Helps Mobile' rules[->15.1] for Amy helps MB
973
///     Else
974
///       Exit with no (RepSystem) effect.
975
///
976
void NPC::repsys_on_help( Character* helped )
×
977
{
978
  if ( Core::settingsManager.repsys_cfg.Hooks.OnHelp )
×
979
  {
980
    if ( Core::settingsManager.repsys_cfg.Hooks.OnHelp->call( this->make_ref(),
×
981
                                                              helped->make_ref() ) )
×
982
      return;
×
983
  }
984

985
  // repsys_on_npc_helps( helped );
986
  if ( master() )
×
987
    master_->Character::repsys_on_help( helped );
×
988
}
989

990
unsigned char Character::hilite_color_idx( const Character* seen_by ) const
102✔
991
{
992
  return Core::RepSystem::hilite_color_idx( seen_by, this );
102✔
993
}
994
unsigned short Character::name_color( const Character* seen_by ) const
12✔
995
{
996
  return Core::RepSystem::name_color( seen_by, this );
12✔
997
}
998

999
///
1000
/// [16] NPC Highlighting
1001
///   If the NPC has a master,
1002
///     Highlight color is the same as the Master's.
1003
///   Else the NPC is invul
1004
///     Hilite them invul is ssopt.cfg option says to.
1005
///   Else
1006
///     Highlight Good NPCs   INNOCENT (Blue)
1007
///     Highlight Neutral NPCs  ATTACKABLE (Grey)
1008
///     Highlight Evil NPCs   MURDERER (Red)
1009
///
1010
unsigned char NPC::hilite_color_idx( const Character* seen_by ) const
46✔
1011
{
1012
  if ( Core::settingsManager.repsys_cfg.Hooks.HighLightColor )
46✔
1013
  {
1014
    NPC* t_amy = const_cast<NPC*>( this );
×
1015
    Character* t_bob = const_cast<Character*>( seen_by );
×
1016
    int hook_highlight = Core::settingsManager.repsys_cfg.Hooks.HighLightColor->call_long(
×
1017
        t_amy->make_ref(), t_bob->make_ref() );
×
1018
    if ( hook_highlight != -1 )
×
1019
      return (unsigned char)( hook_highlight );
×
1020
  }
1021

1022
  if ( Core::settingsManager.ssopt.invul_tag == 2 )
46✔
1023
  {
1024
    if ( invul() )
×
1025
      return CHAR_HILITE_INVUL;
×
1026
  }
1027
  if ( master() )
46✔
1028
  {
1029
    return Core::RepSystem::hilite_color_idx( seen_by, master() );
×
1030
  }
1031

1032
  switch ( template_->alignment )
46✔
1033
  {
1034
  case Core::NpcTemplate::NEUTRAL:
46✔
1035
    return CHAR_HILITE_ATTACKABLE;
46✔
1036
  case Core::NpcTemplate::GOOD:
×
1037
    if ( is_murderer() )
×
1038
      return CHAR_HILITE_MURDERER;
×
1039
    else if ( is_criminal() )
×
1040
      return CHAR_HILITE_ATTACKABLE;
×
1041
    else
1042
      return CHAR_HILITE_INNOCENT;
×
1043
  case Core::NpcTemplate::EVIL:
×
1044
    return CHAR_HILITE_MURDERER;
×
1045
  }
1046
  return CHAR_HILITE_ATTACKABLE;
×
1047
}
1048

1049
unsigned short NPC::name_color( const Character* seen_by ) const
40✔
1050
{
1051
  if ( Core::settingsManager.repsys_cfg.Hooks.NameColor )
40✔
1052
  {
1053
    NPC* t_amy = const_cast<NPC*>( this );
×
1054
    Character* t_bob = const_cast<Character*>( seen_by );
×
1055
    int hook_color = Core::settingsManager.repsys_cfg.Hooks.NameColor->call_long(
×
1056
        t_amy->make_ref(), t_bob->make_ref() );
×
1057
    if ( hook_color != -1 )
×
1058
      return (unsigned short)hook_color;
×
1059
  }
1060

1061
  if ( Core::settingsManager.ssopt.invul_tag == 2 )
40✔
1062
  {
1063
    if ( this->invul() )
×
1064
      return Core::settingsManager.repsys_cfg.NameColoring.Invulnerable;
×
1065
  }
1066
  if ( master() )
40✔
1067
  {
1068
    return Core::RepSystem::name_color( seen_by, master() );
18✔
1069
  }
1070

1071
  switch ( template_->alignment )
22✔
1072
  {
1073
  case Core::NpcTemplate::NEUTRAL:
22✔
1074
    return Core::settingsManager.repsys_cfg.NameColoring.Attackable;
22✔
1075
  case Core::NpcTemplate::GOOD:
×
1076
    if ( is_murderer() )
×
1077
      return Core::settingsManager.repsys_cfg.NameColoring.Murderer;
×
1078
    else if ( is_criminal() )
×
1079
      return Core::settingsManager.repsys_cfg.NameColoring.Criminal;
×
1080
    else
1081
      return Core::settingsManager.repsys_cfg.NameColoring.Innocent;
×
1082
  case Core::NpcTemplate::EVIL:
×
1083
    return Core::settingsManager.repsys_cfg.NameColoring.Murderer;
×
1084
  }
1085

1086
  return Core::settingsManager.repsys_cfg.NameColoring.Attackable;
×
1087
}
1088

1089
///
1090
/// To Set Amy Criminal (For a LevelOfOffense)
1091
///
1092
///  If the LevelOfOffense is 0,
1093
///     clear the CriminalTimer
1094
///  Else
1095
///     Set her Criminal Timer for (LevelOfOffense * CriminalFlagInterval) seconds
1096
///
1097

1098
void Character::make_criminal( int level )
×
1099
{
1100
  set_dirty();
×
1101
  bool was_criminal = is_criminal();
×
1102

1103
  if ( level )
×
1104
  {
1105
    Core::polclock_t timeout_at =
1106
        Core::polclock() + level *
×
1107
                               static_cast<Core::polclock_t>(
×
1108
                                   Core::settingsManager.repsys_cfg.General.CriminalFlagInterval ) *
×
1109
                               Core::POLCLOCKS_PER_SEC;
×
1110

1111
    restart_criminal_timer( timeout_at );
×
1112
    Core::RepSystem::schedule_repsys_task( this, timeout_at + 1 );
×
1113
  }
1114
  else
1115
  {
1116
    clear_criminal_timer();
×
1117
    Core::RepSystem::schedule_repsys_task( this, Core::polclock() - 1 );
×
1118
  }
1119

1120
  if ( !orphan() && was_criminal != is_criminal() )
×
1121
  {
1122
    // Changed to update!
1123
    on_criminal_changed();
×
1124
  }
1125
}
×
1126

1127
void Character::make_murderer( bool newval )
1✔
1128
{
1129
  bool refresh = ( mob_flags_.get( MOB_FLAGS::MURDERER ) != newval );
1✔
1130

1131
  set_dirty();
1✔
1132
  mob_flags_.change( MOB_FLAGS::MURDERER, newval );
1✔
1133

1134
  if ( !orphan() && refresh )
1✔
1135
  {
1136
    // Changed to update!
1137
    on_murderer_changed();
1✔
1138
  }
1139
  Core::RepSystem::schedule_repsys_task( this, Core::polclock() - 1 );
1✔
1140
}
1✔
1141

1142
void Character::make_aggressor_to( Character* chr )
×
1143
{
1144
  Core::polclock_t aggr_timeout_at =
1145
      Core::polclock() +
×
1146
      Core::settingsManager.repsys_cfg.General.AggressorFlagTimeout * Core::POLCLOCKS_PER_SEC;
×
1147
  restart_aggressor_timer( chr, aggr_timeout_at );
×
1148

1149
  // Changed to update!
1150
  on_aggressor_changed();
×
1151

1152
  Core::RepSystem::schedule_repsys_task( this, aggr_timeout_at + 1 );
×
1153
}
×
1154

1155
void Character::make_lawfullydamaged_to( Character* chr )
×
1156
{
1157
  Core::polclock_t aggr_timeout_at =
1158
      Core::polclock() +
×
1159
      Core::settingsManager.repsys_cfg.General.AggressorFlagTimeout * Core::POLCLOCKS_PER_SEC;
×
1160
  restart_lawfully_damaged_timer( chr, aggr_timeout_at );
×
1161

1162
  // Changed to update!
1163
  on_lawfullydamaged_changed();
×
1164

1165
  Core::RepSystem::schedule_repsys_task( this, aggr_timeout_at + 1 );
×
1166
}
×
1167

1168
// add serial to Bob's ToBeReportable list
1169
void Character::add_to_be_reportable( u32 repserial )
1✔
1170
{
1171
  set_dirty();
1✔
1172
  to_be_reportable_.insert( repserial );
1✔
1173
}
1✔
1174
void Character::clear_to_be_reportables()
38✔
1175
{
1176
  set_dirty();
38✔
1177
  to_be_reportable_.clear();
38✔
1178
}
38✔
1179
void Character::commit_to_reportables()
74✔
1180
{
1181
  set_dirty();
74✔
1182
  Core::polclock_t now = Core::polclock();
74✔
1183

1184
  ToBeReportableList::iterator itr = to_be_reportable_.begin(), end = to_be_reportable_.end();
74✔
1185
  for ( ; itr != end; ++itr )
75✔
1186
  {
1187
    reportable_t rt;
1188
    rt.serial = ( *itr );
1✔
1189
    rt.polclock = now;
1✔
1190
    reportable_.insert( rt );
1✔
1191
  }
1192

1193
  to_be_reportable_.clear();
74✔
1194
}
74✔
1195
void Character::clear_reportable( u32 repserial, Core::polclock_t when )
×
1196
{
1197
  set_dirty();
×
1198
  reportable_t rt;
1199
  rt.serial = repserial;
×
1200
  rt.polclock = when;
×
1201
  reportable_.erase( rt );
×
1202
}
×
1203
void Character::clear_my_aggressors()
74✔
1204
{
1205
  // we don't actually have a list of aggressors.
1206
  ToBeReportableList::iterator itr = to_be_reportable_.begin(), end = to_be_reportable_.end();
74✔
1207
  for ( ; itr != end; ++itr )
75✔
1208
  {
1209
    u32 repserial = ( *itr );
1✔
1210
    Character* aggressor = Core::system_find_mobile( repserial );
1✔
1211
    if ( aggressor != nullptr )
1✔
1212
    {
1213
      aggressor->remove_as_aggressor_to( this );
1✔
1214
    }
1215
  }
1216
}
74✔
1217
void Character::remove_as_aggressor_to( Character* chr )
1✔
1218
{
1219
  Core::CharacterRef ref( chr );
1✔
1220
  aggressor_to_.erase( ref );
1✔
1221
  if ( chr->client != nullptr )
1✔
1222
  {
1223
    send_create_mobile_if_nearby_cansee( chr->client, this );
×
1224
  }
1225
  if ( client != nullptr )
1✔
1226
  {
1227
    send_create_mobile_if_nearby_cansee( client, chr );
×
1228
  }
1229
}
1✔
1230
void Character::clear_my_lawful_damagers()
74✔
1231
{
1232
  // we don't actually have a list of aggressors.
1233
  ToBeReportableList::iterator itr = to_be_reportable_.begin(), end = to_be_reportable_.end();
74✔
1234
  for ( ; itr != end; ++itr )
75✔
1235
  {
1236
    u32 repserial = ( *itr );
1✔
1237
    Character* damager = Core::system_find_mobile( repserial );
1✔
1238
    if ( damager != nullptr )
1✔
1239
    {
1240
      damager->remove_as_lawful_damager( this );
1✔
1241
    }
1242
  }
1243
}
74✔
1244

1245
void Character::remove_as_lawful_damager( Character* chr )
1✔
1246
{
1247
  Core::CharacterRef ref( chr );
1✔
1248
  lawfully_damaged_.erase( ref );
1✔
1249

1250
  if ( chr->client != nullptr )
1✔
1251
  {
1252
    send_create_mobile_if_nearby_cansee( chr->client, this );
×
1253
  }
1254
  if ( client != nullptr )
1✔
1255
  {
1256
    send_create_mobile_if_nearby_cansee( client, chr );
×
1257
  }
1258
}
1✔
1259

1260
// Keep these as separate functions if there will be in the future something specific for them
1261
// For now they are just copies of each other
1262

1263
void Character::on_criminal_changed()
×
1264
{
1265
  if ( this->client )
×
1266
    send_move( this->client, this );
×
1267
  send_create_mobile_to_nearby_cansee( this );
×
1268
}
×
1269

1270
void Character::on_murderer_changed()
1✔
1271
{
1272
  if ( this->client )
1✔
1273
    send_move( this->client, this );
×
1274
  send_create_mobile_to_nearby_cansee( this );
1✔
1275
}
1✔
1276

1277
void Character::on_aggressor_changed()
×
1278
{
1279
  if ( this->client )
×
1280
    send_move( this->client, this );
×
1281
  send_create_mobile_to_nearby_cansee( this );
×
1282
}
×
1283

1284
void Character::on_lawfullydamaged_changed()
×
1285
{
1286
  if ( this->client )
×
1287
    send_move( this->client, this );
×
1288
  send_create_mobile_to_nearby_cansee( this );
×
1289
}
×
1290
}  // namespace Mobile
1291
}  // namespace Pol
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

© 2026 Coveralls, Inc