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

polserver / polserver / 13163655424

05 Feb 2025 06:02PM UTC coverage: 58.476% (+0.4%) from 58.057%
13163655424

push

github

web-flow
Expansion helper classes for central handling of features (#756)

* use uoexpansion enums for A9, B9 flags
added TOL to uoexpansion
currently unsure where this will end

simplified account initialisation, added errorcheck

* Expansion classes for server and account
fixed flags

* use of AccountExpansion,ServerExpansion
extended tests
splitted unittest file
added missing test for Min/MaxAttackRange

* more micro performance for world saving
use all 4 cores in CI builds
fix compiler flag warning on windows

* save test for weight_multiplier

* enable gothic,rustictiles when using HSA expansion

* removed ancient unused files

* addressed comments
renamed ServerExpansion to ServerFeatures
added method for expansion name

* got rid of client UOExpansionFlag which is the same as the accout
expansion

* updated core-changes and docs

* updated generated ssopt

* test for tooltips
changed method names
removed/clarified comments

* npc hitchance was never saved
added missing npc save tests

* hitchance bug exists also for items...
added missing test for movemode

465 of 525 new or added lines in 30 files covered. (88.57%)

16 existing lines in 7 files now uncovered.

41768 of 71427 relevant lines covered (58.48%)

388521.09 hits per line

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

39.41
/pol-core/pol/statmsg.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2006/05/23 Shinigami: send_full_statmsg() updated to support Elfs
5
 * - 2009/07/23 MuadDib:   updates for new Enum::Packet Out ID
6
 * - 2009/08/09 MuadDib:   Re factor of Packet 0x25, 0x11 for naming convention
7
 * - 2009/09/06 Turley:    Changed Version checks to bitfield client->ClientType
8
 * - 2009/12/02 Turley:    fixed 0x11 packet (race flag)
9
 */
10

11

12
#include "statmsg.h"
13

14
#include "../clib/clib.h"
15
#include "../clib/rawtypes.h"
16
#include "globals/network.h"
17
#include "globals/settings.h"
18
#include "mobile/charactr.h"
19
#include "network/client.h"
20
#include "network/packethelper.h"
21
#include "network/packets.h"
22
#include "ufunc.h"
23
#include "uoclient.h"
24

25

26
namespace Pol
27
{
28
namespace Core
29
{
30
using namespace Network::PktHelper;
31

32
void send_full_statmsg( Network::Client* client, Mobile::Character* chr )
59✔
33
{
34
  PacketOut<Network::PktOut_11> msg;
59✔
35
  msg->offset += 2;  // msglen
59✔
36
  msg->Write<u32>( chr->serial_ext );
59✔
37
  msg->Write( Clib::strUtf8ToCp1252( chr->name() ).c_str(), 30, false );
59✔
38
  bool ignore_caps = Core::settingsManager.ssopt.core_ignores_defence_caps;
59✔
39
  if ( networkManager.uoclient_general.hits.any )
59✔
40
  {
41
    int v = chr->vital( networkManager.uoclient_general.hits.id ).current_ones();
59✔
42
    if ( v > 0xFFFF )
59✔
43
      v = 0xFFFF;
×
44
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );  // hits
59✔
45
    v = chr->vital( networkManager.uoclient_general.hits.id ).maximum_ones();
59✔
46
    if ( v > 0xFFFF )
59✔
47
      v = 0xFFFF;
×
48
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );  // max_hits
59✔
49
  }
50
  else
51
  {
52
    msg->WriteFlipped<u16>( 0u );  // hits
×
53
    msg->WriteFlipped<u16>( 0u );  // max_hits
×
54
  }
55
  msg->Write<u8>( 0u );  // (client->chr->can_rename( chr ) ? 0xFF : 0);
59✔
56
  if ( client->ClientType & Network::CLIENTTYPE_70300 )
59✔
57
    msg->Write<u8>( 6u );  // New entries for classic client
×
58
  else if ( client->acctSupports( Plib::ExpansionVersion::ML ) &&
59✔
59
            ( client->ClientType & Network::CLIENTTYPE_5000 ) )
×
60
    msg->Write<u8>( 5u );  // Set to ML level
×
61
  else if ( client->acctSupports( Plib::ExpansionVersion::AOS ) )
59✔
62
    msg->Write<u8>( 4u );  // Set to AOS level statbar for full info
59✔
63
  else
UNCOV
64
    msg->Write<u8>( 1u );  // Set to oldschool statbar info.
×
65

66
  // if (chr->race == Plib::RACE_ELF)
67
  //  msg->Write(static_cast<u8>(chr->gender | FLAG_RACE));
68
  // else
69
  msg->Write<u8>( chr->gender );  // GENDER_MALE or GENDER_FEMALE (see ../plib/uconst.h)
59✔
70

71
  if ( networkManager.uoclient_general.strength.any )
59✔
72
  {
73
    int v = chr->attribute( networkManager.uoclient_general.strength.id ).effective();
59✔
74
    if ( v > 0xFFFF )
59✔
75
      v = 0xFFFF;
×
76
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
77
  }
78
  else
79
    msg->WriteFlipped<u16>( 0u );
×
80

81
  if ( networkManager.uoclient_general.dexterity.any )
59✔
82
  {
83
    int v = chr->attribute( networkManager.uoclient_general.dexterity.id ).effective();
59✔
84
    if ( v > 0xFFFF )
59✔
85
      v = 0xFFFF;
×
86
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
87
  }
88
  else
89
    msg->WriteFlipped<u16>( 0u );
×
90

91
  if ( networkManager.uoclient_general.intelligence.any )
59✔
92
  {
93
    int v = chr->attribute( networkManager.uoclient_general.intelligence.id ).effective();
59✔
94
    if ( v > 0xFFFF )
59✔
95
      v = 0xFFFF;
×
96
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
97
  }
98
  else
99
    msg->WriteFlipped<u16>( 0u );
×
100

101
  if ( networkManager.uoclient_general.stamina.any )
59✔
102
  {
103
    int v = chr->vital( networkManager.uoclient_general.stamina.id ).current_ones();
59✔
104
    if ( v > 0xFFFF )
59✔
105
      v = 0xFFFF;
×
106
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
107

108
    v = chr->vital( networkManager.uoclient_general.stamina.id ).maximum_ones();
59✔
109
    if ( v > 0xFFFF )
59✔
110
      v = 0xFFFF;
×
111
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
112
  }
113
  else
114
  {
115
    msg->WriteFlipped<u16>( 0u );
×
116
    msg->WriteFlipped<u16>( 0u );
×
117
  }
118

119
  if ( networkManager.uoclient_general.mana.any )
59✔
120
  {
121
    int v = chr->vital( networkManager.uoclient_general.mana.id ).current_ones();
59✔
122
    if ( v > 0xFFFF )
59✔
123
      v = 0xFFFF;
×
124
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
125

126
    v = chr->vital( networkManager.uoclient_general.mana.id ).maximum_ones();
59✔
127
    if ( v > 0xFFFF )
59✔
128
      v = 0xFFFF;
×
129
    msg->WriteFlipped<u16>( static_cast<u16>( v ) );
59✔
130
  }
131
  else
132
  {
133
    msg->WriteFlipped<u16>( 0u );
×
134
    msg->WriteFlipped<u16>( 0u );
×
135
  }
136

137
  msg->WriteFlipped<u32>( chr->gold_carried() );
59✔
138
  // Adjusted to work with Physical Resist if AOS client, and AOS Resistances enabled.
139
  if ( client->acctSupports( Plib::ExpansionVersion::AOS ) && client->aosresist )
59✔
140
  {
141
    s16 value = chr->physical_resist().sum();
×
142
    if ( chr->has_physical_resist_cap() && !ignore_caps )
×
143
    {
144
      auto cap = chr->physical_resist_cap().sum();
×
145
      value = std::min( cap, value );
×
146
    }
147
    msg->WriteFlipped<s16>( value );
×
148
  }
149
  else
150
    msg->WriteFlipped<u16>( chr->ar() );
59✔
151

152
  unsigned int weight = chr->weight();
59✔
153
  Clib::sanitize_upperlimit( &weight, 0xFFFFu );
59✔
154
  msg->WriteFlipped<u16>( weight );
59✔
155

156
  // moreinfo 5
157
  if ( client->acctSupports( Plib::ExpansionVersion::ML ) &&
59✔
158
       ( client->ClientType & Network::CLIENTTYPE_5000 ) )
×
159
  {
160
    msg->WriteFlipped<u16>( chr->carrying_capacity() );
×
161
    msg->Write<u8>( chr->race + 1u );
×
162
  }
163

164
  // moreinfo 3 start
165
  if ( client->acctSupports( Plib::ExpansionVersion::AOS ) )
59✔
166
  {
167
    msg->WriteFlipped<s16>( chr->skillstatcap().statcap );
59✔
168
    auto follow_value = chr->followers();
59✔
169
    msg->Write<s8>( follow_value.followers );
59✔
170
    msg->Write<s8>( follow_value.followers_max );
59✔
171
    // moreinfo 4 start
172
    s16 value = chr->fire_resist().sum();
59✔
173
    if ( chr->has_fire_resist_cap() && !ignore_caps )
59✔
174
    {
175
      auto cap = chr->fire_resist_cap().sum();
×
176
      value = std::min( cap, value );
×
177
    }
178
    msg->WriteFlipped<s16>( value );
59✔
179
    value = chr->cold_resist().sum();
59✔
180
    if ( chr->has_cold_resist_cap() && !ignore_caps )
59✔
181
    {
182
      auto cap = chr->cold_resist_cap().sum();
×
183
      value = std::min( cap, value );
×
184
    }
185
    msg->WriteFlipped<s16>( value );
59✔
186
    value = chr->poison_resist().sum();
59✔
187
    if ( chr->has_poison_resist_cap() && !ignore_caps )
59✔
188
    {
189
      auto cap = chr->poison_resist_cap().sum();
×
190
      value = std::min( cap, value );
×
191
    }
192
    msg->WriteFlipped<s16>( value );
59✔
193
    value = chr->energy_resist().sum();
59✔
194
    if ( chr->has_energy_resist_cap() && !ignore_caps )
59✔
195
    {
196
      auto cap = chr->energy_resist_cap().sum();
×
197
      value = std::min( cap, value );
×
198
    }
199
    msg->WriteFlipped<s16>( value );
59✔
200
    msg->WriteFlipped<u16>( static_cast<u16>( chr->luck().sum() ) );
59✔
201

202
    msg->WriteFlipped<u16>( chr->min_weapon_damage() );
59✔
203
    msg->WriteFlipped<u16>( chr->max_weapon_damage() );
59✔
204
    msg->WriteFlipped<s32>( chr->tithing() );
59✔
205
  }
206

207
  // Add the new entries as 0's for now
208
  if ( client->ClientType & Network::CLIENTTYPE_70300 )
59✔
209
  {
210
    // msg->offset += 30;
211

212
    msg->WriteFlipped<u16>(
×
213
        static_cast<u16>( chr->physical_resist_cap().sum() ) );  // Physical resist cap
×
214
    msg->WriteFlipped<u16>( static_cast<u16>( chr->fire_resist_cap().sum() ) );  // Fire resist cap
×
215
    msg->WriteFlipped<u16>( static_cast<u16>( chr->cold_resist_cap().sum() ) );  // Cold resist cap
×
216
    msg->WriteFlipped<u16>(
×
217
        static_cast<u16>( chr->poison_resist_cap().sum() ) );  // Poison resist cap
×
218
    msg->WriteFlipped<u16>(
×
219
        static_cast<u16>( chr->energy_resist_cap().sum() ) );  // Energy resist cap
×
220
    s16 value = chr->defence_increase().sum();
×
221
    if ( chr->has_defence_increase_cap() && !ignore_caps )
×
222
    {
223
      auto cap = chr->defence_increase_cap().sum();
×
224
      value = std::min( cap, value );
×
225
    }
226
    msg->WriteFlipped<s16>( value );  // Defense chance increase
×
227
    msg->WriteFlipped<u16>(
×
228
        static_cast<u16>( chr->defence_increase_cap().sum() ) );  // Defense chance cap increase
×
229
    msg->WriteFlipped<u16>( static_cast<u16>( chr->hit_chance().sum() ) );  // Hit chance increase
×
230
    msg->WriteFlipped<u16>(
×
231
        static_cast<u16>( chr->swing_speed_increase().sum() ) );  // swing_speed_increase
×
232
    msg->offset += 2;                                             // damage_increase
×
233
    msg->WriteFlipped<u16>(
×
234
        static_cast<u16>( chr->lower_reagent_cost().sum() ) );  // Lower reagent cost
×
235
    msg->WriteFlipped<u16>(
×
236
        static_cast<u16>( chr->spell_damage_increase().sum() ) );  // Spell damage increase
×
237
    msg->WriteFlipped<u16>(
×
238
        static_cast<u16>( chr->faster_cast_recovery().sum() ) );  // Faster cast recovery
×
239
    msg->WriteFlipped<u16>( static_cast<u16>( chr->faster_casting().sum() ) );   // Faster casting
×
240
    msg->WriteFlipped<u16>( static_cast<u16>( chr->lower_mana_cost().sum() ) );  // Lower mana cost
×
241
  }
242

243
  u16 len = msg->offset;
59✔
244

245
  msg->offset = 1;
59✔
246
  msg->WriteFlipped<u16>( len );
59✔
247
  msg.Send( client, len );
59✔
248

249
  if ( settingsManager.ssopt.send_stat_locks )
59✔
250
    send_stat_locks( client, chr );
×
251
}
59✔
252

253
void send_stat_locks( Network::Client* client, Mobile::Character* chr )
×
254
{
255
  if ( client->getversiondetail().major < 3 )  // only in AOS, I think
×
256
    return;
×
257

258
  u8 lockbit = 0;
×
259

260
  lockbit |= chr->attribute( networkManager.uoclient_general.strength.id ).lock()
×
261
             << 4;  // XX SS DD II (2 bits for each lock)
×
262
  lockbit |= chr->attribute( networkManager.uoclient_general.dexterity.id ).lock() << 2;
×
263
  lockbit |= chr->attribute( networkManager.uoclient_general.intelligence.id ).lock();
×
264

265
  PacketOut<Network::PktOut_BF_Sub19> msg;
×
266
  msg->WriteFlipped<u16>( 12u );
×
267
  msg->offset += 2;         // sub
×
268
  msg->Write<u8>( 0x02u );  // 2D Client = 0x02, KR = 0x05
×
269
  msg->Write<u32>( chr->serial_ext );
×
270
  msg->offset++;  // unk
×
271
  msg->Write<u8>( lockbit );
×
272
  msg.Send( client );
×
273
}
×
274

275
void send_short_statmsg( Network::Client* client, Mobile::Character* chr )
×
276
{
277
  PacketOut<Network::PktOut_11> msg;
×
278
  msg->offset += 2;  // msglen
×
279
  msg->Write<u32>( chr->serial_ext );
×
280
  msg->Write( Clib::strUtf8ToCp1252( chr->name() ).c_str(), 30, false );
×
281

282
  if ( networkManager.uoclient_general.hits.any )
×
283
  {
284
    msg->WriteFlipped<u16>( static_cast<u16>(
×
285
        chr->vital( networkManager.uoclient_general.hits.id ).current_thousands() ) );
×
286
    msg->WriteFlipped<u16>( 1000u );  // max_hits
×
287
  }
288
  else
289
  {
290
    msg->WriteFlipped<u16>( 0u );  // hits
×
291
    msg->WriteFlipped<u16>( 0u );  // max_hits
×
292
  }
293
  msg->Write<u8>( client->chr->can_rename( chr ) ? 0xFFu : 0u );
×
294
  msg->Write<u8>( 0u );  // moreinfo
×
295

296
  u16 len = msg->offset;
×
297
  msg->offset = 1;
×
298
  msg->WriteFlipped<u16>( len );
×
299

300
  msg.Send( client, len );
×
301
}
×
302

303
void send_update_hits_to_inrange( Mobile::Character* chr )
×
304
{
305
  PacketOut<Network::PktOut_A1> msg;
×
306
  msg->Write<u32>( chr->serial_ext );
×
307

308
  if ( networkManager.uoclient_general.hits.any )
×
309
  {
310
    int h = chr->vital( networkManager.uoclient_general.hits.id ).current_ones();
×
311
    if ( h > 0xFFFF )
×
312
      h = 0xFFFF;
×
313
    int mh = chr->vital( networkManager.uoclient_general.hits.id ).maximum_ones();
×
314
    if ( mh > 0xFFFF )
×
315
      mh = 0xFFFF;
×
316
    msg->WriteFlipped<u16>( static_cast<u16>( mh ) );
×
317
    msg->WriteFlipped<u16>( static_cast<u16>( h ) );
×
318

319
    // Send proper data to self (if we exist?)
320
    if ( chr->client && chr->client->ready )
×
321
      msg.Send( chr->client );
×
322

323
    // To stop "HP snooping"...
324
    msg->offset = 5;
×
325
    msg->WriteFlipped<u16>( 1000u );
×
326
    msg->WriteFlipped<u16>( static_cast<u16>( h * 1000 / mh ) );
×
327
  }
328
  else
329
  {
330
    msg->offset += 4;  // hits,maxhits=0
×
331
    if ( chr->client && chr->client->ready )
×
332
      msg.Send( chr->client );
×
333
  }
334

335
  // Exclude self... otherwise their status-window shows 1000 hp!! >_<
336
  transmit_to_others_inrange( chr, &msg->buffer, msg->offset );
×
337
}
×
338
}  // namespace Core
339
}  // 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