Sfoglia il codice sorgente

Merge branch 'master' into feature/map_zones

Aleos 11 mesi fa
parent
commit
f0a1847377
100 ha cambiato i file con 3677 aggiunte e 1897 eliminazioni
  1. 4 2
      .github/workflows/build_servers_gcc.yml
  2. 2 2
      .github/workflows/build_servers_msbuild.yml
  3. 27 0
      conf/battle/instance.conf
  4. 1 1
      conf/battle/items.conf
  5. 7 0
      conf/battle/misc.conf
  6. 4 0
      conf/battle/pet.conf
  7. 3 6
      conf/battle/player.conf
  8. 15 12
      conf/battle_athena.conf
  9. 1 1
      conf/msg_conf/map_msg.conf
  10. 1 1
      conf/msg_conf/map_msg_idn.conf
  11. 1 1
      conf/msg_conf/map_msg_por.conf
  12. 1 1
      conf/msg_conf/map_msg_spn.conf
  13. 1 1
      db/pre-re/skill_db.yml
  14. 229 191
      db/re/item_combos.yml
  15. 448 75
      db/re/item_db_equip.yml
  16. 374 22
      db/re/item_db_etc.yml
  17. 31 32
      db/re/item_db_usable.yml
  18. 206 4
      db/re/item_group_db.yml
  19. 6 2
      db/re/job_stats.yml
  20. 36 0
      db/re/mob_db.yml
  21. 1 0
      db/re/pet_db.yml
  22. 20 1
      db/re/skill_db.yml
  23. 29 8
      db/re/status.yml
  24. 14 2
      doc/ea_job_system.txt
  25. 1 1
      doc/item_bonus.txt
  26. 16 11
      doc/script_commands.txt
  27. 1 0
      doc/skill_db.txt
  28. 2 0
      sql-files/mob_db.sql
  29. 2 0
      sql-files/mob_db2.sql
  30. 2 0
      sql-files/mob_db2_re.sql
  31. 2 0
      sql-files/mob_db_re.sql
  32. 16 0
      sql-files/upgrades/upgrade_20240519.sql
  33. 3 0
      src/char/char-server.vcxproj
  34. 13 4
      src/char/char.cpp
  35. 2 1
      src/char/char.hpp
  36. 81 12
      src/char/char_clif.cpp
  37. 4 4
      src/char/char_logif.cpp
  38. 4 4
      src/char/char_mapif.cpp
  39. 1 1
      src/char/int_achievement.cpp
  40. 9 9
      src/char/int_clan.cpp
  41. 5 3
      src/char/int_guild.cpp
  42. 26 22
      src/char/int_quest.cpp
  43. 10 7
      src/char/inter.cpp
  44. 3 0
      src/common/common-minicore.vcxproj
  45. 3 0
      src/common/common.vcxproj
  46. 4 4
      src/common/grfio.cpp
  47. 1 2
      src/common/grfio.hpp
  48. 4 3
      src/common/mmo.hpp
  49. 4 4
      src/login/account.cpp
  50. 3 0
      src/login/login-server.vcxproj
  51. 42 24
      src/map/atcommand.cpp
  52. 91 77
      src/map/battle.cpp
  53. 7 1
      src/map/battle.hpp
  54. 1 1
      src/map/battleground.cpp
  55. 3 2
      src/map/buyingstore.cpp
  56. 1 1
      src/map/cashshop.cpp
  57. 3 1
      src/map/cashshop.hpp
  58. 3 2
      src/map/chrif.cpp
  59. 63 51
      src/map/clan.cpp
  60. 8 8
      src/map/clan.hpp
  61. 248 308
      src/map/clif.cpp
  62. 35 30
      src/map/clif.hpp
  63. 12 38
      src/map/clif_packetdb.hpp
  64. 271 164
      src/map/guild.cpp
  65. 9 10
      src/map/guild.hpp
  66. 5 3
      src/map/instance.cpp
  67. 1 1
      src/map/instance.hpp
  68. 23 26
      src/map/intif.cpp
  69. 6 6
      src/map/intif.hpp
  70. 60 24
      src/map/itemdb.cpp
  71. 5 1
      src/map/itemdb.hpp
  72. 3 0
      src/map/map-server-generator.vcxproj
  73. 3 0
      src/map/map-server.vcxproj
  74. 84 41
      src/map/map.cpp
  75. 4 1
      src/map/map.hpp
  76. 180 145
      src/map/mob.cpp
  77. 9 7
      src/map/mob.hpp
  78. 52 53
      src/map/npc.cpp
  79. 2 2
      src/map/npc.hpp
  80. 124 0
      src/map/packets.hpp
  81. 187 129
      src/map/party.cpp
  82. 8 8
      src/map/party.hpp
  83. 53 23
      src/map/pc.cpp
  84. 5 4
      src/map/pc.hpp
  85. 71 69
      src/map/pet.cpp
  86. 1 1
      src/map/pet.hpp
  87. 206 106
      src/map/script.cpp
  88. 1 0
      src/map/script.hpp
  89. 13 2
      src/map/script_constants.hpp
  90. 3 3
      src/map/searchstore.cpp
  91. 29 38
      src/map/skill.cpp
  92. 2 2
      src/map/skill.hpp
  93. 24 5
      src/map/status.cpp
  94. 7 4
      src/map/status.hpp
  95. 1 1
      src/map/storage.cpp
  96. 10 8
      src/map/unit.cpp
  97. 0 1
      src/map/unit.hpp
  98. 3 2
      src/map/vending.cpp
  99. 8 6
      src/tool/csv2yaml.cpp
  100. 3 0
      src/tool/csv2yaml.vcxproj

+ 4 - 2
.github/workflows/build_servers_gcc.yml

@@ -49,8 +49,10 @@ jobs:
 
       - name: Command - configure
         env:
-            CONFIGURE_FLAGS: 'CC=gcc-${{ matrix.gcc }} CXX=g++-${{ matrix.gcc }} --enable-buildbot=yes'
-        run: ./configure $CONFIGURE_FLAGS
+            CONFIGURE_FLAGS: 'CC=gcc-${{ matrix.gcc }} CXX=g++-${{ matrix.gcc }}'
+        # -Werror: to treat all warnings as errors
+        # -Wno-error=builtin-declaration-mismatch: otherwise ./configure checks fail
+        run: ./configure $CONFIGURE_FLAGS --enable-buildbot=yes CXXFLAGS='-Werror -Wno-error=builtin-declaration-mismatch'
       
       - name: Command - make clean
         run: make clean

+ 2 - 2
.github/workflows/build_servers_msbuild.yml

@@ -41,8 +41,8 @@ jobs:
 
       - name: Build solution in Debug
         if: ${{ matrix.mode == 'PRE' }}
-        run: msbuild rAthena.sln -t:rebuild -property:Configuration=Debug /p:DefineConstants="BUILDBOT%3BPRERE"
+        run: msbuild rAthena.sln -t:rebuild -property:Configuration=Debug /p:DefineConstants="BUILDBOT%3BPRERE" /warnaserror
 
       - name: Build solution in Debug
         if: ${{ matrix.mode == 'RE' }}
-        run: msbuild rAthena.sln -t:rebuild -property:Configuration=Debug /p:DefineConstants="BUILDBOT"
+        run: msbuild rAthena.sln -t:rebuild -property:Configuration=Debug /p:DefineConstants="BUILDBOT" /warnaserror

+ 27 - 0
conf/battle/instance.conf

@@ -0,0 +1,27 @@
+//--------------------------------------------------------------
+// rAthena Battle Configuration File
+// Originally Translated by Peter Kieser <pfak@telus.net>
+// Made in to plainer English by Ancyker
+//--------------------------------------------------------------
+// Note 1: Value is a config switch (on/off, yes/no or 1/0)
+// Note 2: Value is in percents (100 means 100%)
+// Note 3: Value is a bit field. If no description is given,
+//         assume unit types (1: Pc, 2: Mob, 4: Pet, 8: Homun)
+//--------------------------------------------------------------
+
+// Block leaving for parties, guilds or clans if they have an active instance?
+// Default: yes (Official)
+instance_block_leave: yes
+
+// Block leader changes for parties or guilds if they have an active instance?
+// Default: yes (Official)
+instance_block_leaderchange: yes
+
+// Block inviting for parties or guilds if they have an active instance?
+// This also blocks joining parties, guilds or clans that have a running instance.
+// Default: yes (Official)
+instance_block_invite: yes
+
+// Block expulsion for parties or guilds if they have an active instance?
+// Default: yes (Official)
+instance_block_expulsion: yes

+ 1 - 1
conf/battle/items.conf

@@ -93,7 +93,7 @@ allow_equip_restricted_item: yes
 // Default on official servers: yes for Pre-renewal, no for Renewal
 //item_enabled_npc: yes
 
-// Allow map_flooritem to check if item is droppable? (Note 1)
+// Allow map_addflooritem to check if item is droppable? (Note 1)
 // If yes, undroppable items will be destroyed instead of appearing on the map when a player's inventory is full.
 // Default: yes
 item_flooritem_check: yes

+ 7 - 0
conf/battle/misc.conf

@@ -182,3 +182,10 @@ hide_fav_sell: no
 // affects teleportation. Set this to 1 if you want it to be closer to the old emulator behavior.
 // Valid values: 1-40
 map_edge_size: 15
+
+// When a player drops items, can they stack on the same cell? (Note 1)
+// Officially there's no limit on how many items you can drop on the same cell.
+// If you set this to "no", when you drop an item, it will only drop on a cell that has no item on it yet.
+// A free cell will be searched for in eight directions. If no free cell could be found in those eight tries,
+// then dropping the item will fail (the item stays in the player's inventory).
+item_stacking: yes

+ 4 - 0
conf/battle/pet.conf

@@ -41,6 +41,10 @@ pet_hungry_delay_rate: 100
 // These bonuses are unofficial and found in the import/pet_db.yml
 pet_equip_required: yes
 
+// Should the pet equipment be destroyed if the owner doesn't have enough space in their inventory? (Note 1)
+// Official behavior is "yes", setting this to "no" will leave the item equipped. 
+pet_unequip_destroy: yes
+
 // When the master attacks a monster, whether or not the pet will also attack. (Note 1)
 pet_attack_support: no
 

+ 3 - 6
conf/battle/player.conf

@@ -293,12 +293,9 @@ trait_points_job_change: 7
 // Official is 100.
 max_trait_parameter: 100
 
-// Max amount of RES/MRES to take into the resistance damage reduction formula.
-// A setting of 625 means the max reduction of damage allowed is 50.0%.
-// Formula is 100 - 100 * (5000 + RES) / (5000 + 10 * RES)
-// Note: Best to leave this setting alone unless you know what your doing.
-// Default: 625
-max_res_mres_reduction: 625
+// Max percent of RES/MRES that can be ignored by item bonus/skill.
+// Default: 50
+max_res_mres_ignored: 50
 
 // Maximum AP
 // Default: 1000

+ 15 - 12
conf/battle_athena.conf

@@ -8,6 +8,9 @@
 //General battle-related settings.
 import: conf/battle/battle.conf
 
+//Battleground settings
+import: conf/battle/battleground.conf
+
 //Settings specific to the client.
 import: conf/battle/client.conf
 
@@ -17,14 +20,20 @@ import: conf/battle/drops.conf
 //Experience rates, exp penalties, stats and max level settings.
 import: conf/battle/exp.conf
 
+//Feature control (on/off) settings
+import: conf/battle/feature.conf
+
 //GM levels, atcommands and hack-related configs.
 import: conf/battle/gm.conf
 
 //Guild and WoE settings
 import: conf/battle/guild.conf
 
-//Battleground settings
-import: conf/battle/battleground.conf
+//Homunc related configuration
+import: conf/battle/homunc.conf
+
+//Instance settings
+import: conf/battle/instance.conf
 
 //Item/card-specific and crafting related options.
 import: conf/battle/items.conf
@@ -32,15 +41,16 @@ import: conf/battle/items.conf
 //Mob related configuration
 import: conf/battle/monster.conf
 
+// Anything else that didn't fit anywhere else.
+// Includes duel, day/night, mute/manner, log settings.
+import: conf/battle/misc.conf
+
 //Party related configuration
 import: conf/battle/party.conf
 
 //Pet related configuration
 import: conf/battle/pet.conf
 
-//Homunc related configuration
-import: conf/battle/homunc.conf
-
 //Player specific settings
 import: conf/battle/player.conf
 
@@ -50,12 +60,5 @@ import: conf/battle/skill.conf
 //Status change related settings
 import: conf/battle/status.conf
 
-//Feature control (on/off) settings
-import: conf/battle/feature.conf
-
-// Anything else that didn't fit anywhere else.
-// Includes duel, day/night, mute/manner, log settings.
-import: conf/battle/misc.conf
-
 //Your custom config goes here.
 import: conf/import/battle_conf.txt

+ 1 - 1
conf/msg_conf/map_msg.conf

@@ -775,7 +775,7 @@
 730: Character cannot be disguised while in monster form.
 731: Transforming into monster is not allowed in Guild Wars.
 
-732: Item cannot be opened when your inventory is full.
+//732: Free
 
 733: Please enter a NPC file name (usage: @reloadnpcfile <file name>).
 

+ 1 - 1
conf/msg_conf/map_msg_idn.conf

@@ -765,7 +765,7 @@
 730: Karakter tidak dapat disguise ketika sedang berwujud monster.
 731: Perubahan menjadi monster tidak diizinkan dalam Guild Wars.
 
-732: Item tidak dapat dibuka ketika inventory penuh.
+//732: Free
 
 //733 free
 

+ 1 - 1
conf/msg_conf/map_msg_por.conf

@@ -779,7 +779,7 @@
 730: O personagem não pode ser disfarçado enquanto estiver em forma de monstro.
 731: Transformar em monstro não é permitido em GvG.
 
-732: O item não pode ser aberto quando o seu inventário está cheio.
+//732: Free
 
 733: Por favor insira um nome de arquivo NPC (uso: @reloadnpcfile <nome do arquivo>).
 

+ 1 - 1
conf/msg_conf/map_msg_spn.conf

@@ -775,7 +775,7 @@
 730: El personaje no puede disfrazarse si está transformado en un monstruo.
 731: No puedes transformarte en monstruo durante la guerra de clanes.
 
-732: No puedes abrir el objeto porque tu inventario está lleno.
+//732: libre
 
 733: Introduce la ruta de archivo de un NPC (instrucciones: @reloadnpcfile <ruta>).
 

+ 1 - 1
db/pre-re/skill_db.yml

@@ -7359,7 +7359,7 @@ Body:
         Time: 1500
       - Level: 5
         Time: 1000
-    Duration2: 300000
+    Duration1: 300000
     Requires:
       SpCost: 1
       Weapon:

File diff suppressed because it is too large
+ 229 - 191
db/re/item_combos.yml


File diff suppressed because it is too large
+ 448 - 75
db/re/item_db_equip.yml


+ 374 - 22
db/re/item_db_etc.yml

@@ -14232,7 +14232,7 @@ Body:
     Type: Card
     SubType: Enchant
     Script: |
-      autobonus2 "{ bonus bStr,200; bonus2 bHPLossRate,500,1000; }",20,5000,BF_WEAPON,"{ active_transform 1060,5000; specialeffect2 EF_POTION_BERSERK; showscript \"Bigfoot Power !\"; }";
+      autobonus2 "{ bonus bStr,200; bonus2 bHPLossRate,500,1000; }",20,5000,BF_WEAPON,"{ specialeffect2 EF_POTION_BERSERK; }";
     UnEquipScript: |
       heal 0,-300;
   - Id: 4876
@@ -29172,21 +29172,18 @@ Body:
     Type: Etc
     Buy: 5
     Weight: 1
-    Attack: 10
   - Id: 7664
     AegisName: Shooting_Mine
     Name: Grenade Launcher
     Type: Etc
     Buy: 30
     Weight: 1
-    Attack: 10
   - Id: 7665
     AegisName: Dragon_Tail_Missile
     Name: Dragon Tail Missile
     Type: Etc
     Buy: 15
     Weight: 1
-    Attack: 10
   - Id: 7666
     AegisName: TimeTravel_Scroll
     Name: Time Travel Scroll
@@ -43794,13 +43791,131 @@ Body:
     Script: |
       bonus2 bWeaponDamageRate,W_BOW,10;
       bonus bCritical,10;
+  - Id: 29127
+    AegisName: Mana_Vermilion
+    Name: Red Lotus
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Fire,10;
+      bonus2 bMagicAtkEle,Ele_Fire,10;
+      bonus2 bSubEle,Ele_Fire,5;
+  - Id: 29128
+    AegisName: Mana_Braze
+    Name: Brass
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Water,10;
+      bonus2 bMagicAtkEle,Ele_Water,10;
+      bonus2 bSubEle,Ele_Water,5;
+  - Id: 29129
+    AegisName: Mana_Chrome
+    Name: Silver
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Wind,10;
+      bonus2 bMagicAtkEle,Ele_Wind,10;
+      bonus2 bSubEle,Ele_Wind,5;
+  - Id: 29130
+    AegisName: Mana_Twilight
+    Name: Dusk
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Earth,10;
+      bonus2 bMagicAtkEle,Ele_Earth,10;
+      bonus2 bSubEle,Ele_Earth,5;
+  - Id: 29131
+    AegisName: Mana_Chaos
+    Name: Chaos
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Dark,10;
+      bonus2 bMagicAtkEle,Ele_Dark,10;
+      bonus2 bSubEle,Ele_Dark,5;
+  - Id: 29132
+    AegisName: Mana_Rise
+    Name: Dawn
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddEle,Ele_Holy,10;
+      bonus2 bMagicAtkEle,Ele_Holy,10;
+      bonus2 bSubEle,Ele_Holy,5;
+  - Id: 29133
+    AegisName: Mana_Heal
+    Name: Recover
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bHealPower,10;
+      bonus bHealPower2,10;
+      bonus bAddItemHealRate,10;
+  - Id: 29134
+    AegisName: Heart_Of_Winner
+    Name: Thoughts of a Champion
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bAddRace,RC_DemiHuman,5;
+      bonus2 bMagicAddRace,RC_DemiHuman,5;
+  - Id: 29135
+    AegisName: Keen1
+    Name: Sharp 1
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bCritical,3;
+      bonus bCritAtkRate,1;
+  - Id: 29136
+    AegisName: Keen2
+    Name: Sharp 2
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bCritical,6;
+      bonus bCritAtkRate,2;
+  - Id: 29137
+    AegisName: Keen3
+    Name: Sharp 3
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bCritical,9;
+      bonus bCritAtkRate,3;
+  - Id: 29138
+    AegisName: Keen4
+    Name: Sharp 4
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bCritical,12;
+      bonus bCritAtkRate,4;
+  - Id: 29139
+    AegisName: Keen5
+    Name: Sharp 5
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus bCritical,15;
+      bonus bCritAtkRate,5;
   - Id: 29140
     AegisName: Orcish_Hero_Of_Anger
     Name: Anger of the Brave
     Type: Card
     SubType: Enchant
     Script: |
-      autobonus "{ bonus3 bAutoSpell,\"NPC_EARTHQUAKE\",3,1000; }",5,5000,BF_NORMAL,"{ active_transform 1850,5000; }";
+      autobonus "{ bonus3 bAutoSpell,\"NPC_EARTHQUAKE\",3,1000; }",8,5000,BF_WEAPON,"{ active_transform 1850,5000; }";
+  - Id: 29141
+    AegisName: Roar_Of_Baphomet
+    Name: Demon Sheep's Roar
+    Type: Card
+    SubType: Enchant
+    Script: |
+      autobonus "{ bonus3 bAutoSpell,\"NPC_HELLJUDGEMENT\",6,1000; }",8,5000,BF_WEAPON,"{ active_transform 1929,5000; }";
   - Id: 29142
     AegisName: Electric_Effect
     Name: Electric Effect
@@ -46274,25 +46389,39 @@ Body:
       bonus2 bSkillAtk,"SC_FATALMENACE",15;
   - Id: 29619
     AegisName: Shadowchaser_Bottom2
-    Name: Shadow Chaser Stone II (Bottom)
+    Name: Stalker Stone II (Lower)
     Type: Card
     SubType: Enchant
     Script: |
       bonus bHit,getskilllv("TF_MISS");
   - Id: 29620
     AegisName: Shadowchaser_Middle2
-    Name: Shadow Chaser Stone II (Middle)
+    Name: Stalker Stone II (Middle)
     Type: Card
     SubType: Enchant
     Script: |
       bonus2 bSkillAtk,"RG_BACKSTAP",15;
   - Id: 29621
     AegisName: Shadowchaser_Top2
-    Name: Shadow Chaser Stone II (Upper)
+    Name: Stalker Stone II (Upper)
     Type: Card
     SubType: Enchant
     Script: |
       bonus bBaseAtk,2*getskilllv("RG_SNATCHER");
+  - Id: 29647
+    AegisName: Lucky_Amulet7
+    Name: Traitor
+    Type: Card
+    SubType: Enchant
+    Script: |
+      if (readparam(bStr)>=110)
+         bonus2 bSkillCooldown,"RL_SLUGSHOT",-10000;
+      if (readparam(bAgi)>=110)
+         bonus2 bSkillAtk,"RL_R_TRIP",30*(readparam(bAgi)/5);
+      if (readparam(bInt)>=110)
+         bonus2 bSkillCooldown,"RL_HAMMER_OF_GOD",-20000;
+      if (readparam(bLuk)>=110)
+         bonus2 bSkillAtk,"RL_D_TAIL",30*(readparam(bLuk)/5);
   - Id: 29651
     AegisName: Soullinker_Top
     Name: Soul Linker Stone (Upper)
@@ -48585,7 +48714,7 @@ Body:
       bonus bHit,-50;
   - Id: 300140
     AegisName: ILL_Sropho_Card
-    Name: Deep Sea Sropho Card
+    Name: Abysmal Sropho Card
     Type: Card
     Weight: 10
     Locations:
@@ -48603,7 +48732,7 @@ Body:
       }
   - Id: 300141
     AegisName: ILL_Obeaune_Card
-    Name: Deep Sea Obeaune Card
+    Name: Abysmal Obeaune Card
     Type: Card
     Weight: 10
     Locations:
@@ -48614,7 +48743,7 @@ Body:
       bonus bMatk,30;
   - Id: 300142
     AegisName: ILL_Deviace_Card
-    Name: Deep Sea Deviace Card
+    Name: Abysmal Deviace Card
     Type: Card
     Weight: 10
     Locations:
@@ -48633,7 +48762,7 @@ Body:
       }
   - Id: 300143
     AegisName: ILL_Marse_Card
-    Name: Deep Sea Marse Card
+    Name: Abysmal Marse Card
     Type: Card
     Weight: 10
     Locations:
@@ -48645,7 +48774,7 @@ Body:
       bonus2 bSubRace,RC_Insect,15;
   - Id: 300144
     AegisName: ILL_Merman_Card
-    Name: Deep Sea Merman Card
+    Name: Abysmal Merman Card
     Type: Card
     Weight: 10
     Locations:
@@ -48656,7 +48785,7 @@ Body:
       bonus bBaseAtk,4*(readparam(bStr)/15);
   - Id: 300145
     AegisName: ILL_Abysmal_Witch_Card
-    Name: Deep Sea Witch Card
+    Name: Abysmal Witch Card
     Type: Card
     Weight: 10
     Locations:
@@ -48669,7 +48798,7 @@ Body:
       bonus2 bSubEle,Ele_All,-30;
   - Id: 300146
     AegisName: ILL_Sedora_Card
-    Name: Deep Sea Sedora Card
+    Name: Abysmal Sedora Card
     Type: Card
     Weight: 10
     Locations:
@@ -48680,7 +48809,7 @@ Body:
       bonus2 bCriticalAddRace,RC_Fish,30;
   - Id: 300147
     AegisName: ILL_Sword_Fish_Card
-    Name: Deep Sea Swordfish Card
+    Name: Abysmal Swordfish Card
     Type: Card
     Weight: 10
     Locations:
@@ -48696,7 +48825,7 @@ Body:
       }
   - Id: 300148
     AegisName: ILL_Strouf_Card
-    Name: Deep Sea Strouf Card
+    Name: Abysmal Strouf Card
     Type: Card
     Weight: 10
     Locations:
@@ -48712,7 +48841,7 @@ Body:
       }
   - Id: 300149
     AegisName: ILL_Phen_Card
-    Name: Deep Sea Phen Card
+    Name: Abysmal Phen Card
     Type: Card
     Weight: 10
     Locations:
@@ -48724,7 +48853,7 @@ Body:
       bonus2 bMagicAtkEle,Ele_Wind,3*getrefine();
   - Id: 300150
     AegisName: ILL_King_Dramoh_Card
-    Name: Deep Sea King Dramoh Card
+    Name: Abysmal King Dramoh Card
     Type: Card
     Weight: 10
     Locations:
@@ -48738,7 +48867,7 @@ Body:
       bonus bAtkRate,(getrefine()/3);
   - Id: 300151
     AegisName: ILL_Kraken_Card
-    Name: Deep Sea Kraken Card
+    Name: Abysmal Kraken Card
     Type: Card
     Weight: 10
     Locations:
@@ -50467,8 +50596,8 @@ Body:
     Script: |
       bonus2 bAddRace,RC_Demon,40;
       bonus2 bAddRace,RC_DemiHuman,40;
-      bonus3 bSubRace,RC_Demon,-30,BF_NORMAL;
-      bonus3 bSubRace,RC_DemiHuman,-30,BF_NORMAL;
+      bonus3 bSubRace,RC_Demon,-30,BF_WEAPON;
+      bonus3 bSubRace,RC_DemiHuman,-30,BF_WEAPON;
   - Id: 300281
     AegisName: R001_Bestia_Card
     Name: R001-Bestia Card
@@ -81508,6 +81637,229 @@ Body:
          bonus bCritAtkRate,(.@param/10);
          bonus bCritical,(.@param/10);
       }
+  - Id: 313269
+    AegisName: aegis_313269
+    Name: Latent Release (Abyss Chaser VII)
+    Type: Card
+    SubType: Enchant
+  - Id: 313289
+    AegisName: aegis_313289
+    Name: Latent Awakening (Duple Light I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"AB_DUPLELIGHT_MELEE",BaseLevel*2;
+      bonus2 bSkillAtk,"AB_DUPLELIGHT_MAGIC",BaseLevel*2;
+  - Id: 313290
+    AegisName: aegis_313290
+    Name: Latent Awakening (Counter Slash I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"GC_COUNTERSLASH",BaseLevel/10;
+  - Id: 313292
+    AegisName: aegis_313292
+    Name: Latent Awakening (Rolling Cutter II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"GC_ROLLINGCUTTER",BaseLevel/2;
+  - Id: 313293
+    AegisName: aegis_313293
+    Name: Latent Awakening (Cross Slash II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"KO_JYUMONJIKIRI",BaseLevel/3;
+  - Id: 313294
+    AegisName: aegis_313294
+    Name: Latent Awakening (Swirling Petal II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"KO_HUUMARANKA",BaseLevel/4;
+  - Id: 313295
+    AegisName: aegis_313295
+    Name: Latent Awakening (Shield Press II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"LG_SHIELDPRESS",BaseLevel/2;
+  - Id: 313296
+    AegisName: aegis_313296
+    Name: Latent Awakening (Ray of Genesis II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"LG_RAYOFGENESIS",BaseLevel/3;
+  - Id: 313297
+    AegisName: aegis_313297
+    Name: Latent Awakening (Severe Rainstorm III)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"WM_SEVERE_RAINSTORM",BaseLevel/3;
+  - Id: 313298
+    AegisName: aegis_313298
+    Name: Latent Awakening (Arms Cannon II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"NC_ARMSCANNON",BaseLevel/5;
+  - Id: 313299
+    AegisName: aegis_313299
+    Name: Latent Awakening (Axe Tornado II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"NC_AXETORNADO",BaseLevel/2;
+  - Id: 313300
+    AegisName: aegis_313300
+    Name: Latent Awakening (Axe Boomerang II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"NC_AXEBOOMERANG",BaseLevel/2;
+  - Id: 313301
+    AegisName: aegis_313301
+    Name: Latent Awakening (Arrow Storm III)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RA_ARROWSTORM",BaseLevel/10;
+  - Id: 313302
+    AegisName: aegis_313302
+    Name: Latent Awakening (Aimed Bolt II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RA_AIMEDBOLT",BaseLevel/10;
+  - Id: 313303
+    AegisName: aegis_313303
+    Name: Latent Awakening (Ignition Break III)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RK_IGNITIONBREAK",BaseLevel/2;
+  - Id: 313304
+    AegisName: aegis_313304
+    Name: Latent Awakening (Slug Shot I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RL_SLUGSHOT",BaseLevel/3;
+  - Id: 313305
+    AegisName: aegis_313305
+    Name: Latent Awakening (Hammer of God I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RL_HAMMER_OF_GOD",BaseLevel/3;
+  - Id: 313306
+    AegisName: aegis_313306
+    Name: Latent Awakening (Anti-Material Blast I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RL_AM_BLAST",BaseLevel/3;
+  - Id: 313307
+    AegisName: aegis_313307
+    Name: Latent Awakening (Round Trip II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RL_R_TRIP",BaseLevel/2;
+  - Id: 313308
+    AegisName: aegis_313308
+    Name: Latent Awakening (Fire Rain II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"RL_FIRE_RAIN",BaseLevel/2;
+  - Id: 313309
+    AegisName: aegis_313309
+    Name: Latent Awakening (Triangle Shot II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SC_TRIANGLESHOT",BaseLevel/3;
+  - Id: 313310
+    AegisName: aegis_313310
+    Name: Latent Awakening (Varetyr Spear II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SO_VARETYR_SPEAR",BaseLevel/3;
+  - Id: 313311
+    AegisName: aegis_313311
+    Name: Latent Awakening (Diamond Dust I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SO_DIAMONDDUST",BaseLevel/3;
+  - Id: 313312
+    AegisName: aegis_313312
+    Name: Latent Awakening (Earth Grave I)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SO_EARTHGRAVE",BaseLevel/3;
+  - Id: 313313
+    AegisName: aegis_313313
+    Name: Latent Awakening (Espa II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SP_SPA",BaseLevel/5;
+  - Id: 313314
+    AegisName: aegis_313314
+    Name: Latent Awakening (Eswoo II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SP_SWHOO",BaseLevel/5;
+  - Id: 313315
+    AegisName: aegis_313315
+    Name: Latent Awakening (Curse Explosion II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SP_CURSEEXPLOSION",BaseLevel/5;
+  - Id: 313316
+    AegisName: aegis_313316
+    Name: Latent Awakening (Lion's Howl II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SR_HOWLINGOFLION",BaseLevel/3;
+  - Id: 313317
+    AegisName: aegis_313317
+    Name: Latent Awakening (Sky Blow II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"SR_SKYNETBLOW",BaseLevel/2;
+  - Id: 313318
+    AegisName: aegis_313318
+    Name: Latent Awakening (Soul Expansion II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"WL_SOULEXPANSION",BaseLevel/5;
+  - Id: 313319
+    AegisName: aegis_313319
+    Name: Latent Awakening (Crimson Rock II)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      bonus2 bSkillAtk,"WL_CRIMSONROCK",BaseLevel/5;
+  - Id: 313320
+    AegisName: aegis_313320
+    Name: Skill Stone (Firewall Lv.10)
+    Type: Card
+    SubType: Enchant
+    Script: |
+      skill "MG_FIREWALL",10;
   - Id: 313324
     AegisName: Biolo_Robe
     Name: Biolo Stone (Garment)

+ 31 - 32
db/re/item_db_usable.yml

@@ -5700,7 +5700,6 @@ Body:
     AegisName: Tropical_Sograt
     Name: Tropical Sograt
     Type: Usable
-    Buy: 1000
     Weight: 100
     Flags:
       BuyingStore: true
@@ -5710,7 +5709,6 @@ Body:
     AegisName: Vermilion_The_Beach
     Name: Vermilion on the Beach
     Type: Usable
-    Buy: 1000
     Weight: 100
     Flags:
       BuyingStore: true
@@ -51130,8 +51128,8 @@ Body:
       NoGuildStorage: true
       NoMail: true
       NoAuction: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      getitem 5464,1;
   - Id: 17203
     AegisName: Free_Pass_Box
     Name: Free Pass Box
@@ -51457,7 +51455,6 @@ Body:
     Type: Usable
     Buy: 10
     Weight: 10
-    Defense: 4
     Trade:
       NoDrop: true
       NoTrade: true
@@ -60451,7 +60448,7 @@ Body:
       getitem 526,100; /* Royal Jelly */
   - Id: 23042
     AegisName: Seed_Of_Yggdrasil_
-    Name: "[Not For Sale]Yggdrasil Seed"
+    Name: "[Not For Sale] Yggdrasil Seed"
     Type: Healing
     Weight: 300
     Delay:
@@ -63091,7 +63088,6 @@ Body:
     AegisName: Vend_Arbeit1_1Lv
     Name: "[1 Day] Consignment Merchant Envelope Lv1"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63100,7 +63096,6 @@ Body:
     AegisName: Vend_Arbeit1_2Lv
     Name: "[1 Day] Consignment Merchant Envelope Lv2"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63109,7 +63104,6 @@ Body:
     AegisName: Vend_Arbeit1_3Lv
     Name: "[1 Day] Consignment Merchant Envelope Lv3"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63118,7 +63112,6 @@ Body:
     AegisName: Buy_Arbeit1_1Lv
     Name: "[1 Day] Personal Shopper Envelope Lv1"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63127,7 +63120,6 @@ Body:
     AegisName: Buy_Arbeit1_2Lv
     Name: "[1 Day] Personal Shopper Envelope Lv2"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63136,7 +63128,6 @@ Body:
     AegisName: Buy_Arbeit1_3Lv
     Name: "[1 Day] Personal Shopper Envelope Lv3"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63235,7 +63226,6 @@ Body:
     AegisName: Vend_Arbeit3_1Lv
     Name: "[3 Day] Consignment Merchant Envelope Lv1"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63244,7 +63234,6 @@ Body:
     AegisName: Vend_Arbeit3_2Lv
     Name: Consignment Merchant Envelope
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63253,7 +63242,6 @@ Body:
     AegisName: Vend_Arbeit3_3Lv
     Name: "[3 Day] Consignment Merchant Envelope Lv3"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63262,7 +63250,6 @@ Body:
     AegisName: Buy_Arbeit3_1Lv
     Name: "[3 Day] Personal Shopper Envelope Lv1"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63271,7 +63258,6 @@ Body:
     AegisName: Buy_Arbeit3_2Lv
     Name: Personal Shopper Envelope
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -63280,7 +63266,6 @@ Body:
     AegisName: Buy_Arbeit3_3Lv
     Name: "[3 Day] Personal Shopper Envelope Lv3"
     Type: Usable
-    Weight: 10
     Flags:
       Container: true
     Script: |
@@ -68477,8 +68462,8 @@ Body:
     Weight: 10
     Flags:
       BuyingStore: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      sc_start SC_EXPBOOST,900000,100;
   - Id: 100352
     AegisName: Real_Battle_Manual
     Name: Real Combat Manual
@@ -68486,8 +68471,9 @@ Body:
     Weight: 10
     Flags:
       BuyingStore: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      sc_start SC_EXPBOOST,3600000,100;
+      sc_start SC_SPEEDUP0,600000,25;
   - Id: 100354
     AegisName: Auto_Armor_Refine_Cube
     Name: Automatic Armor +11 Refinement Cube
@@ -70340,8 +70326,8 @@ Body:
     Weight: 50
     Flags:
       BuyingStore: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      pet 1198;
   - Id: 100810
     AegisName: BroadcastBox_
     Name: Broadcast Box
@@ -72636,8 +72622,8 @@ Body:
     Name: Spring Regulator
     Type: Usable
     Weight: 10
-#   Script: |
-#     /* TODO */
+    Script: |
+      item_enchant(24);
   - Id: 101100
     AegisName: Poenetentia_Box3
     Name: Poenitentia Two Swords Set Box
@@ -73602,6 +73588,7 @@ Body:
     Flags:
       BuyingStore: true
     Script: |
+      /* TODO : pet 21632; */
       pet 1005;
   - Id: 101336
     AegisName: aegis_101336
@@ -76681,23 +76668,31 @@ Body:
     Script: |
       item_reform();
   - Id: 102128
-    AegisName: aegis_102128
+    AegisName: All_In_One_buff
     Name: All-in-One Buff Potion    # !todo check english name
     Type: Usable
     Weight: 10
     Flags:
       BuyingStore: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      specialeffect2 EF_POTION_BERSERK;
+      sc_start SC_M_DEFSCROLL,3600000,0;
+      sc_start SC_LIMIT_POWER_BOOSTER,3600000,30;
+      sc_start SC_INFINITY_DRINK,3600000,0;
+      sc_start SC_SPEEDUP0,600000,25;
   - Id: 102129
-    AegisName: aegis_102129
+    AegisName: All_In_One_Healing
     Name: All-in-one Healing Potion    # !todo check english name
     Type: Usable
     Weight: 10
     Flags:
       BuyingStore: true
-#   Script: |
-#     /* TODO */
+    Script: |
+      specialeffect2 EF_HEAL3;
+      sc_start2 SC_S_LIFEPOTION,3600000,-5,5;
+      sc_start2 SC_L_LIFEPOTION,3600000,-7,4;
+      sc_start2 SC_M_LIFEPOTION,3600000,-4,3;
+      sc_start SC_SPEEDUP0,600000,25;
   - Id: 102193
     AegisName: aegis_102193
     Name: Costume Enchant Stone Box 30
@@ -78647,6 +78642,10 @@ Body:
     Name: Costume Enchant Stone Box 35
     Type: Usable
     Weight: 10
+    Flags:
+      Container: true
+    Script: |
+      getgroupitem(IG_ENCHANT_STONE_BOX35);
   - Id: 103095
     AegisName: Kr_B_Special10
     Name: Love Cake

+ 206 - 4
db/re/item_group_db.yml

@@ -51768,7 +51768,7 @@ Body:
             Item: aegis_19868
             Rate: 50
           - Index: 3
-            Item: aegis_410245
+            Item: C_Brother_Keen_Eye
             Rate: 50
           - Index: 4
             Item: C_Foxtail
@@ -51777,7 +51777,7 @@ Body:
             Item: aegis_400496
             Rate: 50
           - Index: 6
-            Item: aegis_420242
+            Item: C_Fluttering_Haze
             Rate: 50
           - Index: 7
             Item: C_Cat_Mouth
@@ -92258,7 +92258,7 @@ Body:
             Item: Hero_Token_SH
             Rate: 10
           - Index: 17
-            Item: aegis_490220
+            Item: Hero_Token_HN
             Rate: 10
   - Group: JANUARYGIFTBOX_
     SubGroups:
@@ -109579,7 +109579,7 @@ Body:
             Item: Small_Mana_Potion
             Amount: 250
           - Index: 1
-            Item: aegis_420304
+            Item: C_NightSkyOfRutie
           - Index: 2
             Item: K_Secret_Key
             Amount: 30
@@ -112975,3 +112975,205 @@ Body:
             Item: E_EXP_Drop_Up_7days
           - Index: 2
             Item: Comp_AID_Buff_Box
+  - Group: ENCHANT_STONE_BOX35
+    SubGroups:
+      - SubGroup: 1
+        List:
+          - Index: 0
+            Item: Range_Stone_Robe_D
+            Rate: 10
+          - Index: 1
+            Item: Melee_Stone_Robe_D
+            Rate: 10
+          - Index: 2
+            Item: Magic_Stone_Robe_D
+            Rate: 10
+          - Index: 3
+            Item: SmatkStone_Robe
+            Rate: 10
+          - Index: 4
+            Item: M_PATKStone_Robe
+            Rate: 10
+          - Index: 5
+            Item: R_PATKStone_Robe
+            Rate: 10
+          - Index: 6
+            Item: ResistDefStone_Robe
+            Rate: 10
+          - Index: 7
+            Item: StaminaWISStone_Robe_D
+            Rate: 10
+          - Index: 8
+            Item: POWStone_Robe_D
+            Rate: 10
+          - Index: 9
+            Item: SplStone_Robe_D
+            Rate: 10
+          - Index: 10
+            Item: ConStone_Robe_D
+            Rate: 10
+          - Index: 11
+            Item: CrtStone_Robe_D
+            Rate: 10
+          - Index: 12
+            Item: aegis_1001615
+            Rate: 10
+          - Index: 13
+            Item: BioloStone_Robe
+            Rate: 10
+          - Index: 14
+            Item: InquisitorStone_Robe
+            Rate: 10
+          - Index: 15
+            Item: SpiritHandlerStone_Robe
+            Rate: 10
+          - Index: 16
+            Item: ReloadStone_Robe_D
+            Rate: 10
+          - Index: 17
+            Item: CriticalStone_Robe_D
+            Rate: 10
+          - Index: 18
+            Item: DoubleAttack_Stone
+            Rate: 20
+          - Index: 19
+            Item: Critical_Stone_Robe
+            Rate: 20
+          - Index: 20
+            Item: CastStone_Robe_D
+            Rate: 40
+          - Index: 21
+            Item: SPdrainStone_Robe_D
+            Rate: 40
+          - Index: 22
+            Item: HPdrainStone_Robe_D
+            Rate: 80
+          - Index: 23
+            Item: ASPDStone_Robe_D
+            Rate: 80
+          - Index: 24
+            Item: CastStone_Robe
+            Rate: 80
+          - Index: 25
+            Item: GeneticStone_Top3
+            Rate: 80
+          - Index: 26
+            Item: GeneticStone_Middle3
+            Rate: 80
+          - Index: 27
+            Item: GeneticStone_Bottom3
+            Rate: 80
+          - Index: 28
+            Item: SuraStone_Top3
+            Rate: 80
+          - Index: 29
+            Item: SuraStone_Middle3
+            Rate: 80
+          - Index: 30
+            Item: SuraStone_Bottom3
+            Rate: 80
+          - Index: 31
+            Item: DoramStone_Top3
+            Rate: 80
+          - Index: 32
+            Item: DoramStone_Middle3
+            Rate: 80
+          - Index: 33
+            Item: DoramStone_Bottom3
+            Rate: 80
+          - Index: 34
+            Item: Stone_Robe_Box
+            Rate: 170
+          - Index: 35
+            Item: Stone_Robe3_Box
+            Rate: 170
+          - Index: 36
+            Item: Magic_Stone_Top
+            Rate: 200
+          - Index: 37
+            Item: Magic_Stone_Middle
+            Rate: 200
+          - Index: 38
+            Item: Magic_Stone_Bottom
+            Rate: 200
+          - Index: 39
+            Item: Range_Stone_Top
+            Rate: 200
+          - Index: 40
+            Item: Range_Stone
+            Rate: 200
+          - Index: 41
+            Item: Range_Stone_Bottom
+            Rate: 200
+          - Index: 42
+            Item: Melee_Stone_Top
+            Rate: 200
+          - Index: 43
+            Item: Melee_Stone_Middle
+            Rate: 200
+          - Index: 44
+            Item: Melee_Stone_Bottom
+            Rate: 200
+          - Index: 45
+            Item: DefenseStone_Top
+            Rate: 200
+          - Index: 46
+            Item: DefenseStone_Middle
+            Rate: 200
+          - Index: 47
+            Item: DefenseStone_Bottom
+            Rate: 200
+          - Index: 48
+            Item: ReloadStone_Top
+            Rate: 200
+          - Index: 49
+            Item: ReloadStone_Middle
+            Rate: 200
+          - Index: 50
+            Item: ReloadStone_Bottom
+            Rate: 200
+          - Index: 51
+            Item: EXPStone_Middle
+            Rate: 200
+          - Index: 52
+            Item: EXPStone_Bottom
+            Rate: 200
+          - Index: 53
+            Item: EXPStone_Top
+            Rate: 200
+          - Index: 54
+            Item: Stone_Top_Box
+            Rate: 400
+          - Index: 55
+            Item: Stone_Top2_Box
+            Rate: 400
+          - Index: 56
+            Item: Stone_Middle_Box
+            Rate: 400
+          - Index: 57
+            Item: Stone_Middle2_Box
+            Rate: 400
+          - Index: 58
+            Item: Stone_Bottom_Box
+            Rate: 400
+          - Index: 59
+            Item: Stone_Bottom2_Box
+            Rate: 400
+          - Index: 60
+            Item: CastingStone_Top
+            Rate: 400
+          - Index: 61
+            Item: CastingStone_Middle
+            Rate: 400
+          - Index: 62
+            Item: CastingStone_Bottom
+            Rate: 400
+          - Index: 63
+            Item: Critical_Stone
+            Rate: 400
+          - Index: 64
+            Item: Critical_Stone_Top
+            Rate: 400
+          - Index: 65
+            Item: Critical_Stone_Bottom
+            Rate: 400

+ 6 - 2
db/re/job_stats.yml

@@ -11154,7 +11154,6 @@ Body:
       - Level: 2
         Str: 1
         Agi: 1
-        Sta: 1
       - Level: 3
         Vit: 1
         Luk: 1
@@ -11190,6 +11189,7 @@ Body:
         Dex: 1
       - Level: 15
         Luk: 1
+        Sta: 1
       - Level: 16
         Vit: 1
         Int: 1
@@ -11230,13 +11230,13 @@ Body:
       - Level: 30
         Str: 1
         Int: 1
-        Sta: 1
       - Level: 31
         Dex: 1
         Con: 1
       - Level: 32
         Pow: 1
       - Level: 33
+        Sta: 1
         Crt: 1
       - Level: 34
         Crt: 1
@@ -11251,7 +11251,10 @@ Body:
       - Level: 40
         Pow: 1
       - Level: 41
+        Sta: 1
         Con: 1
+      - Level: 42
+        Sta: 1
       - Level: 43
         Crt: 1
       - Level: 44
@@ -11260,6 +11263,7 @@ Body:
         Con: 1
       - Level: 46
         Pow: 1
+        Sta: 1
       - Level: 47
         Con: 1
       - Level: 48

+ 36 - 0
db/re/mob_db.yml

@@ -93806,6 +93806,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Demon
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Fire
     ElementLevel: 3
     WalkSpeed: 150
@@ -93862,6 +93864,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Brute
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Fire
     ElementLevel: 3
     WalkSpeed: 150
@@ -93908,6 +93912,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Undead
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Undead
     ElementLevel: 1
     WalkSpeed: 200
@@ -93954,6 +93960,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Undead
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Undead
     ElementLevel: 1
     WalkSpeed: 190
@@ -94000,6 +94008,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Demon
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Water
     ElementLevel: 2
     WalkSpeed: 250
@@ -94048,6 +94058,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Undead
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Undead
     ElementLevel: 1
     WalkSpeed: 300
@@ -94096,6 +94108,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Undead
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Dark
     ElementLevel: 1
     WalkSpeed: 140
@@ -94144,6 +94158,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Undead
+    RaceGroups:
+      Illusion_Moonlight: true
     Element: Undead
     ElementLevel: 3
     WalkSpeed: 150
@@ -94363,6 +94379,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Demon
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Water
     ElementLevel: 1
     WalkSpeed: 190
@@ -94409,6 +94427,8 @@ Body:
     ChaseRange: 12
     Size: Large
     Race: Formless
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Water
     ElementLevel: 2
     WalkSpeed: 220
@@ -94453,6 +94473,8 @@ Body:
     ChaseRange: 12
     Size: Large
     Race: Formless
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Water
     ElementLevel: 3
     WalkSpeed: 250
@@ -94497,6 +94519,8 @@ Body:
     ChaseRange: 12
     Size: Small
     Race: Formless
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Water
     ElementLevel: 2
     WalkSpeed: 1000
@@ -94550,6 +94574,8 @@ Body:
     ChaseRange: 12
     Size: Large
     Race: Brute
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Water
     ElementLevel: 4
     WalkSpeed: 400
@@ -94593,6 +94619,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Formless
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Neutral
     ElementLevel: 1
     WalkSpeed: 1000
@@ -94621,6 +94649,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Formless
+    RaceGroups:
+      Illusion_Frozen: true
     Element: Neutral
     ElementLevel: 1
     WalkSpeed: 1000
@@ -96615,6 +96645,8 @@ Body:
     ChaseRange: 12
     Size: Large
     Race: Formless
+    RaceGroups:
+      Illusion_Luanda: true
     Element: Neutral
     ElementLevel: 4
     WalkSpeed: 200
@@ -96643,6 +96675,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Demihuman
+    RaceGroups:
+      Illusion_Luanda: true
     Element: Earth
     ElementLevel: 2
     WalkSpeed: 200
@@ -96671,6 +96705,8 @@ Body:
     ChaseRange: 12
     Size: Medium
     Race: Demihuman
+    RaceGroups:
+      Illusion_Luanda: true
     Element: Fire
     ElementLevel: 2
     WalkSpeed: 200

+ 1 - 0
db/re/pet_db.yml

@@ -2788,6 +2788,7 @@ Body:
 #         bonus bFlee,20;
 #      }
 #
+#  - Mob: FARMILIAR2 #TODO : Implement mob FARMILIAR2
   - Mob: FARMILIAR
     TameItem: Fruits_Set_Trap
     EggItem: Farmiliar_Egg

+ 20 - 1
db/re/skill_db.yml

@@ -7235,6 +7235,7 @@ Body:
     TargetType: Self
     DamageFlags:
       IgnoreDefense: true
+      SimpleDefense: true
     Range: 9
     Hit: Single
     HitCount: 1
@@ -7664,6 +7665,7 @@ Body:
     TargetType: Attack
     DamageFlags:
       IgnoreFlee: true
+      SimpleDefense: true
     Flags:
       TargetTrap: true
     Range: -2
@@ -7697,7 +7699,6 @@ Body:
       - Level: 5
         Time: 1000
     Duration1: 3000
-    Duration2: 300000
     FixedCastTime:
       - Level: 1
         Time: 2000
@@ -9452,6 +9453,7 @@ Body:
     DamageFlags:
       IgnoreAtkCard: true
       IgnoreFlee: true
+      SimpleDefense: true
     Flags:
       IsNpc: true
       TargetSelf: true
@@ -19110,6 +19112,7 @@ Body:
     DamageFlags:
       Splash: true
       IgnoreFlee: true
+      SimpleDefense: true
     Flags:
       TargetTrap: true
     Range: 9
@@ -22655,6 +22658,7 @@ Body:
     DamageFlags:
       Splash: true
       IgnoreFlee: true
+      SimpleDefense: true
     Flags:
       AllowOnMado: true
     Range: 9
@@ -28789,6 +28793,7 @@ Body:
     DamageFlags:
       Splash: true
       IgnoreFlee: true
+      SimpleDefense: true
     Range:
       - Level: 1
         Size: 7
@@ -29687,6 +29692,19 @@ Body:
         - Level: 2
           Amount: 100
     Status: Odins_Power
+  - Id: 2543
+    Name: ALL_RAY_OF_PROTECTION
+    Description: Ray of Protection
+    MaxLevel: 1
+    Type: Magic
+    TargetType: Support
+    DamageFlags:
+      NoDamage: true
+    Range: 1
+    Hit: Single
+    HitCount: 1
+    Duration1: 60000
+    Status: Protection
   - Id: 2544
     Name: MC_CARTDECORATE
     Description: Decorate Cart
@@ -33145,6 +33163,7 @@ Body:
     DamageFlags:
       Splash: true
       IgnoreFlee: true
+      SimpleDefense: true
     Flags:
       TargetTrap: true
     Range: 9

+ 29 - 8
db/re/status.yml

@@ -72,6 +72,7 @@ Body:
       Stun: true
       Sleep: true
       Burning: true
+      Protection: true
     EndOnStart:
       Aeterna: true
     EndReturn:
@@ -125,6 +126,7 @@ Body:
       Stun: true
       Sleep: true
       Burning: true
+      Protection: true
     EndOnStart:
       Aeterna: true
   - Status: Stun
@@ -144,6 +146,7 @@ Body:
       Inspiration: true
       Gvg_Stun: true
       Stun: true
+      Protection: true
   - Status: Sleep
     DurationLookup: NPC_SLEEPATTACK
     States:
@@ -162,6 +165,7 @@ Body:
       Inspiration: true
       Gvg_Sleep: true
       Sleep: true
+      Protection: true
   - Status: Poison
     DurationLookup: NPC_POISON
     CalcFlags:
@@ -180,6 +184,7 @@ Body:
       Inspiration: true
       Poison: true
       Dpoison: true
+      Protection: true
   - Status: Curse
     DurationLookup: NPC_WIDECURSE
     CalcFlags:
@@ -198,6 +203,7 @@ Body:
       Inspiration: true
       Gvg_Curse: true
       Curse: true
+      Protection: true
   - Status: Silence
     DurationLookup: NPC_SILENCEATTACK
     States:
@@ -214,6 +220,7 @@ Body:
       Inspiration: true
       Gvg_Silence: true
       Silence: true
+      Protection: true
   - Status: Confusion
     DurationLookup: NPC_WIDECONFUSE
     Flags:
@@ -223,6 +230,7 @@ Body:
     Fail:
       Refresh: true
       Inspiration: true
+      Protection: true
     EndReturn:
       Confusion: true
   - Status: Blind
@@ -242,6 +250,7 @@ Body:
       Fear: true
       Gvg_Blind: true
       Blind: true
+      Protection: true
   - Status: Bleeding
     Icon: EFST_BLOODING
     DurationLookup: NPC_BLEEDING
@@ -259,6 +268,7 @@ Body:
     Fail:
       Refresh: true
       Inspiration: true
+      Protection: true
   - Status: Dpoison
     DurationLookup: NPC_POISON
     CalcFlags:
@@ -999,6 +1009,7 @@ Body:
     Flags:
       SendOption: true
   - Status: Extremityfist
+    Icon: EFST_EXTREMITYFIST
     DurationLookup: MO_EXTREMITYFIST
     CalcFlags:
       Regen: true
@@ -6107,14 +6118,6 @@ Body:
       NoBanishingBuster: true
       NoDispell: true
       RemoveOnHermode: true
-  - Status: Extremityfist2
-    Icon: EFST_EXTREMITYFIST
-    DurationLookup: MO_EXTREMITYFIST
-    Flags:
-      NoRemoveOnDead: true
-      NoDispell: true
-      NoBanishingBuster: true
-      NoClearance: true
   - Status: Mtf_Aspd2
     Icon: EFST_MTF_ASPD2
     CalcFlags:
@@ -8923,3 +8926,21 @@ Body:
       NoClearance: true
     EndOnStart:
       Decreaseagi: true
+  - Status: Protection
+    Icon: EFST_RAY_OF_PROTECTION
+    Flags:
+      NoClearbuff: true
+      NoDispell: true
+      NoBanishingBuster: true
+      NoClearance: true
+    EndOnStart:
+      Stun: true
+      Sleep: true
+      Curse: true
+      Stone: true
+      Poison: true
+      Blind: true
+      Silence: true
+      Bleeding: true
+      Confusion: true
+      Freeze: true

+ 14 - 2
doc/ea_job_system.txt

@@ -63,11 +63,12 @@ The eA Job System:
 	EAJL_2		0x300
 
 - The third category is type. Classes can either be normal, rebirth/advanced,
-  adopted, or third class.
+  adopted, third class or fourth class.
 
 	EAJL_UPPER	0x1000
 	EAJL_BABY	0x2000
 	EAJL_THIRD	0x4000
+	EAJL_FOURTH	0x8000
 
 So using these three categories, any job class can be constructed from the
 others. Let's take a swordman, for example.
@@ -143,6 +144,11 @@ EAJL_THIRD:
 	if(@eac&EAJL_THIRD)
 		mes "Wow, you've really grown!";
 
+EAJL_FOURTH:
+	Checks if a class is a fourth job.
+	if(@eac&EAJL_FOURTH)
+		mes "Wow, you've really grown!";
+
 EAJ_UPPERMASK:
 	The upper mask can be used to "strip" the upper/baby characteristics of a
 	class, used when you want to know if someone is a certain class regardless
@@ -169,13 +175,19 @@ EAJ_BASEMASK:
 	check will  always fail for the same reasons previously explained.
 
 EAJ_THIRDMASK:
-	This mask strips 3rd class attributes.  It will give the "normal" class of
+	This mask strips 3rd class attributes. It will give the "normal" class of
 	a third job, regardless of rebirth/adopted status.  When used on non-third
 	class characters, it will return the second job, or, if that also doesn't
 	exist, the first.
 	if ((@eac&EAJ_THIRDMASK) == EAJ_WARLOCK_T)
 		mes "You've gone through rebirth, I see.";
 
+EAJ_FOURTHMASK:
+	This mask strips 4th class attributes. Although currently there are none,
+	it is suggested to use this for checking.
+	if ((@eac&EAJ_FOURTHMASK) == EAJ_DRAGON_KNIGHT)
+		mes "Oh you are a Dragon Knight, I see.";
+
 The script commands eaclass, roclass:
 -------------------------------------------------------------------------------
 

+ 1 - 1
doc/item_bonus.txt

@@ -31,7 +31,7 @@ This list contains all available constants referenced in the 'bonus' commands.
 	RC2_Bio5_Swordman_Thief, RC2_Bio5_Acolyte_Merchant, RC2_Bio5_Mage_Archer, RC2_Bio5_MVP,
 	RC2_Clocktower, RC2_Thanatos, RC2_Faceworm, RC2_Hearthunter, RC2_Rockridge, RC2_Werner_Lab,
 	RC2_Temple_Demon, RC2_Illusion_Vampire, RC2_Malangdo, RC2_EP172ALPHA, RC2_EP172BETA, RC2_EP172BATH,
-	RC2_Illusion_Turtle, RC2_Rachel_Sanctuary, RC2_Illusion_Luanda
+	RC2_Illusion_Turtle, RC2_Rachel_Sanctuary, RC2_Illusion_Luanda, RC2_Illusion_Frozen, RC2_Illusion_Moonlight
 
 * Class (c)
 	Class_Normal, Class_Boss, Class_Guardian, Class_All

+ 16 - 11
doc/script_commands.txt

@@ -7962,17 +7962,15 @@ Return values:
 //
 ---------------------------------------
 
-*npcspeed <speed value>;
-*npcwalkto <x>,<y>;
-*npcstop;
+*npcspeed( <speed value> {,"<npc name>"} );
+*npcwalkto( <x>,<y> {,"<npc name>"} } );
+*npcstop( {"<npc name>", {"<flag>"}});
 
-These commands will make the NPC object in question move around the map. As they
-currently are, they are a bit buggy and are not useful for much more than making
-an NPC move randomly around the map.
+These commands will make the NPC object in question move around the map.
 
-'npcspeed' will set the NPCs walking speed to a specified value. As in the
-@speed GM command, 200 is the slowest possible speed while 0 is the fastest
-possible (instant motion). 100 is the default character walking speed.
+'npcspeed' will permanently set the NPCs walking speed to a specified value. As in the
+@speed GM command, MAX_WALK_SPEED (1000) is the slowest possible speed while MIN_WALK_SPEED (20) is the fastest
+possible (instant motion). DEFAULT_NPC_WALK_SPEED (200) is the default NPC walking speed.
 
 'npcwalkto' will start the NPC sprite moving towards the specified coordinates
 on the same map it is currently on. The script proceeds immediately after the
@@ -7980,10 +7978,17 @@ NPC begins moving.
 
 'npcstop' will stop the motion.
 
+The <flag> value in npcstop affects how the unit is stopped. The following flags are bitwise values (can be combined using the pipe operator):
+	USW_NONE = Unit will keep walking to their original destination.
+	USW_FIXPOS = Issue a fixpos packet afterwards.
+	USW_MOVE_ONCE = Force the unit to move one cell if it hasn't yet.
+	USW_MOVE_FULL_CELL = Enable moving to the next cell when unit was already half-way there (may cause on-touch/place side-effects, such as a scripted map change).
+	USW_FORCE_STOP = Force stop moving.
+Default: USW_FIXPOS | USW_MOVE_FULL_CELL | USW_FORCE_STOP
+
 While in transit, the NPC will be clickable, but invoking it will cause it to
 stop moving, which will make its coordinates different from what the client
-computed based on the speed and motion coordinates. The effect is rather
-unnerving.
+computed based on the speed and motion coordinates.
 
 Only a few NPC sprites have walking animations, and those that do, do not get
 the animation invoked when moving the NPC, due to the problem in the NPC walking

+ 1 - 0
doc/skill_db.txt

@@ -58,6 +58,7 @@ IgnoreFlee		- Skill ignores target's flee (Magic type always ignores)
 IgnoreDefCard	- Skill ignores target's defense cards.
 IgnoreLongCard	- Skill ignores caster's long range damage cards.
 Critical		- Skill can critical.
+SimpleDefense	- (Renewal-only) Physical damage is flatly reduced by DEF+DEF2. RES is ignored.
 
 ---------------------------------------
 

+ 2 - 0
sql-files/mob_db.sql

@@ -63,6 +63,8 @@ CREATE TABLE `mob_db` (
   `racegroup_illusion_turtle` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_rachel_sanctuary` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_illusion_luanda` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL,
   `element` varchar(24) DEFAULT NULL,
   `element_level` tinyint(4) unsigned DEFAULT NULL,
   `walk_speed` smallint(6) unsigned DEFAULT NULL,

+ 2 - 0
sql-files/mob_db2.sql

@@ -63,6 +63,8 @@ CREATE TABLE `mob_db2` (
   `racegroup_illusion_turtle` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_rachel_sanctuary` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_illusion_luanda` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL,
   `element` varchar(24) DEFAULT NULL,
   `element_level` tinyint(4) unsigned DEFAULT NULL,
   `walk_speed` smallint(6) unsigned DEFAULT NULL,

+ 2 - 0
sql-files/mob_db2_re.sql

@@ -65,6 +65,8 @@ CREATE TABLE `mob_db2_re` (
   `racegroup_illusion_turtle` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_rachel_sanctuary` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_illusion_luanda` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL,
   `element` varchar(24) DEFAULT NULL,
   `element_level` tinyint(4) unsigned DEFAULT NULL,
   `walk_speed` smallint(6) unsigned DEFAULT NULL,

+ 2 - 0
sql-files/mob_db_re.sql

@@ -65,6 +65,8 @@ CREATE TABLE `mob_db_re` (
   `racegroup_illusion_turtle` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_rachel_sanctuary` tinyint(1) unsigned DEFAULT NULL,
   `racegroup_illusion_luanda` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+  `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL,
   `element` varchar(24) DEFAULT NULL,
   `element_level` tinyint(4) unsigned DEFAULT NULL,
   `walk_speed` smallint(6) unsigned DEFAULT NULL,

+ 16 - 0
sql-files/upgrades/upgrade_20240519.sql

@@ -0,0 +1,16 @@
+ALTER TABLE `mob_db`
+	ADD COLUMN `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+	ADD COLUMN `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL
+;
+ALTER TABLE `mob_db2`
+	ADD COLUMN `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+	ADD COLUMN `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL
+;
+ALTER TABLE `mob_db_re`
+	ADD COLUMN `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+	ADD COLUMN `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL
+;
+ALTER TABLE `mob_db2_re`
+	ADD COLUMN `racegroup_illusion_frozen` tinyint(1) unsigned DEFAULT NULL,
+	ADD COLUMN `racegroup_illusion_moonlight` tinyint(1) unsigned DEFAULT NULL
+;

+ 3 - 0
src/char/char-server.vcxproj

@@ -128,6 +128,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -148,6 +149,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -170,6 +172,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 13 - 4
src/char/char.cpp

@@ -2158,16 +2158,23 @@ int char_pincode_compare( int fd, struct char_session_data* sd, char* pin ){
 	}
 }
 
-
-void char_pincode_decrypt( uint32 userSeed, char* pin ){
+bool char_pincode_decrypt( uint32 userSeed, char* pin ){
 	int i;
 	char tab[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 	char *buf;
-	
+
+	if (safestrnlen(pin, 4) != PINCODE_LENGTH)
+		return false;
+
+	for (i = 0; i < PINCODE_LENGTH; ++i) {
+		if (!ISDIGIT(pin[i]))
+			return false;
+	}
+
 	for( i = 1; i < 10; i++ ){
 		int pos;
 		uint32 multiplier = 0x3498, baseSeed = 0x881234;
-		
+
 		userSeed = baseSeed + userSeed * multiplier;
 		pos = userSeed % ( i + 1 );
 		if( i != pos ){
@@ -2184,6 +2191,8 @@ void char_pincode_decrypt( uint32 userSeed, char* pin ){
 	}
 	strcpy( pin, buf );
 	aFree( buf );
+
+	return true;
 }
 #endif
 

+ 2 - 1
src/char/char.hpp

@@ -266,6 +266,7 @@ struct char_session_data {
 	uint32 pincode_seed;
 	time_t pincode_change;
 	uint16 pincode_try;
+	bool pincode_correct;
 	// Addon system
 	unsigned int char_moves[MAX_CHARS]; // character moves left
 	uint8 isvip;
@@ -318,7 +319,7 @@ int char_family(int pl1,int pl2,int pl3);
 int char_loadName(uint32 char_id, char* name);
 int char_check_char_name(char * name, char * esc_name);
 
-void char_pincode_decrypt( uint32 userSeed, char* pin );
+bool char_pincode_decrypt( uint32 userSeed, char* pin );
 int char_pincode_compare( int fd, struct char_session_data* sd, char* pin );
 void char_auth_ok(int fd, struct char_session_data *sd);
 void char_set_charselect(uint32 account_id);

+ 81 - 12
src/char/char_clif.cpp

@@ -167,15 +167,22 @@ int chclif_parse_pincode_check( int fd, struct char_session_data* sd ){
 
 	char pin[PINCODE_LENGTH+1];
 
-	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
+		set_eof(fd);
 		return 1;
+	}
 
 	memset(pin,0,PINCODE_LENGTH+1);
 	strncpy((char*)pin, RFIFOCP(fd, 6), PINCODE_LENGTH);
 	RFIFOSKIP(fd,10);
 
-	char_pincode_decrypt(sd->pincode_seed, pin );
+	if (!char_pincode_decrypt(sd->pincode_seed, pin )) {
+		set_eof(fd);
+		return 1;
+	}
+
 	if( char_pincode_compare( fd, sd, pin ) ){
+		sd->pincode_correct = true;
 		chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
 	}
 	return 1;
@@ -257,28 +264,34 @@ bool pincode_allowed( char* pincode ){
 int chclif_parse_pincode_change( int fd, struct char_session_data* sd ){
 	FIFOSD_CHECK(14);
 
-	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
+		set_eof(fd);
 		return 1;
+	}
 	else {
 		char oldpin[PINCODE_LENGTH+1];
 		char newpin[PINCODE_LENGTH+1];
-		
+
 		memset(oldpin,0,PINCODE_LENGTH+1);
 		memset(newpin,0,PINCODE_LENGTH+1);
 		strncpy(oldpin, RFIFOCP(fd,6), PINCODE_LENGTH);
 		strncpy(newpin, RFIFOCP(fd,10), PINCODE_LENGTH);
 		RFIFOSKIP(fd,14);
-		
-		char_pincode_decrypt(sd->pincode_seed,oldpin);
+
+		if (!char_pincode_decrypt(sd->pincode_seed,oldpin) || !char_pincode_decrypt(sd->pincode_seed,newpin)) {
+			set_eof(fd);
+			return 1;
+		}
+
 		if( !char_pincode_compare( fd, sd, oldpin ) )
 			return 1;
-		char_pincode_decrypt(sd->pincode_seed,newpin);
 
 		if( pincode_allowed(newpin) ){
 			chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
 			strncpy(sd->pincode, newpin, sizeof(newpin));
 			ShowInfo("Pincode changed for AID: %d\n", sd->account_id);
-		
+			sd->pincode_correct = true;
+
 			chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
 		}else{
 			chclif_pincode_sendstate( fd, sd, PINCODE_ILLEGAL );
@@ -293,21 +306,27 @@ int chclif_parse_pincode_change( int fd, struct char_session_data* sd ){
 int chclif_parse_pincode_setnew( int fd, struct char_session_data* sd ){
 	FIFOSD_CHECK(10);
 
-	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id )
+	if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
+		set_eof(fd);
 		return 1;
+	}
 	else {
 		char newpin[PINCODE_LENGTH+1];
 		memset(newpin,0,PINCODE_LENGTH+1);
 		strncpy( newpin, RFIFOCP(fd,6), PINCODE_LENGTH );
 		RFIFOSKIP(fd,10);
 
-		char_pincode_decrypt( sd->pincode_seed, newpin );
+		if (!char_pincode_decrypt( sd->pincode_seed, newpin )) {
+			set_eof(fd);
+			return 1;
+		}
 
 		if( pincode_allowed(newpin) ){
 			chlogif_pincode_notifyLoginPinUpdate( sd->account_id, newpin );
 			strncpy( sd->pincode, newpin, sizeof( newpin ) );
+			sd->pincode_correct = true;
 
-			chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );	
+			chclif_pincode_sendstate( fd, sd, PINCODE_PASSED );
 		}else{
 			chclif_pincode_sendstate( fd, sd, PINCODE_ILLEGAL );
 		}
@@ -741,6 +760,7 @@ int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){
 		sd->login_id2 = login_id2;
 		sd->sex = sex;
 		sd->auth = false; // not authed yet
+		sd->pincode_correct = false; // not entered pincode correctly yet
 
 		// send back account_id
 		WFIFOHEAD(fd,4);
@@ -763,6 +783,7 @@ int chclif_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){
 		{// authentication found (coming from map server)
 			char_get_authdb().erase(account_id);
 			char_auth_ok(fd, sd);
+			sd->pincode_correct = true; // already entered pincode correctly yet
 		}
 		else
 		{// authentication not found (coming from login server)
@@ -1553,6 +1574,54 @@ int chclif_parse(int fd) {
 		unsigned short cmd;
 
 		cmd = RFIFOW(fd,0);
+
+#if PACKETVER_SUPPORTS_PINCODE
+		// If the pincode system is enabled
+		if( charserv_config.pincode_config.pincode_enabled ){
+			switch( cmd ){
+				// Connect of player
+				case 0x65:
+				// Client keep-alive packet (every 12 seconds)
+				case 0x187:
+				// Checks the entered pin
+				case 0x8b8:
+				// Request PIN change
+				case 0x8be:
+				// Request for PIN window
+				case 0x8c5:
+				// Request character list
+				case 0x9a1:
+				// Connect of map-server
+				case 0x2af8:
+					break;
+
+				// Before processing any other packets, do a few checks
+				default:
+					// To reach this block the client should have attained a session already
+					if( sd != nullptr ){
+						// If the pincode was entered correctly
+						if( sd->pincode_correct ){
+							break;
+						}
+
+						// If no pincode is set (yet)
+						if( strlen( sd->pincode ) <= 0 ){
+							break;
+						}
+
+						// The pincode was not entered correctly, yet the player (=bot) tried to send a different packet => Goodbye!
+						set_eof( fd );
+						return 0;
+					}else{
+						// Unknown packet received
+						ShowError( "chclif_parse: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", cmd, ip2str( ipl, nullptr ) );
+						set_eof( fd );
+						return 0;
+					}
+			}
+		}
+#endif
+
 		switch( cmd ) {
 			case 0x65: next=chclif_parse_reqtoconnect(fd,sd,ipl); break;
 			// char select
@@ -1596,7 +1665,7 @@ int chclif_parse(int fd) {
 				break;
 			// unknown packet received
 			default:
-				ShowError("parse_char: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, nullptr));
+				ShowError( "chclif_parse: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", cmd, ip2str( ipl, nullptr ) );
 				set_eof(fd);
 				return 0;
 		}

+ 4 - 4
src/char/char_logif.cpp

@@ -96,7 +96,7 @@ void chlogif_pincode_start(int fd, struct char_session_data* sd){
 TIMER_FUNC(chlogif_send_acc_tologin){
 	if ( chlogif_isconnected() ){
 		// send account list to login server
-		int users = char_get_onlinedb().size();
+		size_t users = char_get_onlinedb().size();
 		int i = 0;
 
 		WFIFOHEAD(login_fd,8+users*4);
@@ -176,7 +176,7 @@ void chlogif_send_global_accreg(const char *key, unsigned int index, int64 int_v
 	if (!chlogif_isconnected())
 		return;
 
-	int nlen = WFIFOW(login_fd, 2);
+	int16 nlen = WFIFOW( login_fd, 2 );
 	size_t len;
 
 	len = strlen(key)+1;
@@ -185,7 +185,7 @@ void chlogif_send_global_accreg(const char *key, unsigned int index, int64 int_v
 	nlen += 1;
 
 	safestrncpy(WFIFOCP(login_fd,nlen), key, len);
-	nlen += len;
+	nlen += static_cast<decltype(nlen)>( len );
 
 	WFIFOL(login_fd, nlen) = index;
 	nlen += 4;
@@ -201,7 +201,7 @@ void chlogif_send_global_accreg(const char *key, unsigned int index, int64 int_v
 			nlen += 1;
 
 			safestrncpy(WFIFOCP(login_fd,nlen), string_value, len);
-			nlen += len;
+			nlen += static_cast<decltype(nlen)>( len );
 		}
 	} else {
 		WFIFOB(login_fd, nlen) = int_value ? 0 : 1;

+ 4 - 4
src/char/char_mapif.cpp

@@ -195,7 +195,7 @@ void chmapif_send_misc(int fd) {
  * @param map_id
  * @param count Number of map from new map-server has
  **/
-void chmapif_send_maps(int fd, int map_id, int count, unsigned char *mapbuf) {
+void chmapif_send_maps( int fd, int map_id, size_t count, unsigned char* mapbuf ){
 	uint16 x;
 
 	if (count == 0) {
@@ -205,7 +205,7 @@ void chmapif_send_maps(int fd, int map_id, int count, unsigned char *mapbuf) {
 		unsigned char buf[INT16_MAX];
 		// Transmitting maps information to the other map-servers
 		WBUFW(buf,0) = 0x2b04;
-		WBUFW( buf, 2 ) = count * MAP_NAME_LENGTH_EXT + 10;
+		WBUFW( buf, 2 ) = static_cast<int16>( count * MAP_NAME_LENGTH_EXT + 10 );
 		WBUFL(buf,4) = htonl(map_server[map_id].ip);
 		WBUFW(buf,8) = htons(map_server[map_id].port);
 		memcpy( WBUFP( buf, 10 ), mapbuf, count * MAP_NAME_LENGTH_EXT );
@@ -317,7 +317,7 @@ int chmapif_parse_askscdata(int fd){
 				ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid);
 			if (count > 0)
 			{
-				WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data);
+				WFIFOW( fd, 2 ) = static_cast<int16>( 14 + count * sizeof( struct status_change_data ) );
 				WFIFOW(fd,12) = count;
 				WFIFOSET(fd,WFIFOW(fd,2));
 			}
@@ -574,7 +574,7 @@ int chmapif_parse_req_skillcooldown(int fd){
 				ShowWarning("Too many skillcooldowns for %d:%d, some of them were not loaded.\n", aid, cid);
 			if( count > 0 )
 			{
-				WFIFOW(fd,2) = 14 + count * sizeof(struct skill_cooldown_data);
+				WFIFOW( fd, 2 ) = static_cast<int16>( 14 + count * sizeof( struct skill_cooldown_data ) );
 				WFIFOW(fd,12) = count;
 				WFIFOSET(fd,WFIFOW(fd,2));
 				//Clear the data once loaded.

+ 1 - 1
src/char/int_achievement.cpp

@@ -264,7 +264,7 @@ void mapif_achievement_load( int fd, uint32 char_id ){
 
 	WFIFOHEAD(fd, num_achievements * sizeof(struct achievement) + 8);
 	WFIFOW(fd, 0) = 0x3862;
-	WFIFOW(fd, 2) = num_achievements * sizeof(struct achievement) + 8;
+	WFIFOW(fd, 2) = static_cast<int16>( num_achievements * sizeof( struct achievement ) + 8 );
 	WFIFOL(fd, 4) = char_id;
 
 	if (num_achievements > 0)

+ 9 - 9
src/char/int_clan.cpp

@@ -60,9 +60,12 @@ std::shared_ptr<struct clan> inter_clan_fromsql(int clan_id){
 	clan = std::make_shared<struct clan>();
 
 	clan->id = clan_id;
-	Sql_GetData(sql_handle,  0, &data, &len); memcpy(clan->name, data, min(len, NAME_LENGTH));
-	Sql_GetData(sql_handle,  1, &data, &len); memcpy(clan->master, data, min(len, NAME_LENGTH));
-	Sql_GetData(sql_handle,  2, &data, &len); memcpy(clan->map, data, min(len, MAP_NAME_LENGTH_EXT));
+	Sql_GetData( sql_handle,  0, &data, &len );
+	memcpy( clan->name, data, std::min( len, static_cast<decltype(len)>( NAME_LENGTH ) ) );
+	Sql_GetData( sql_handle,  1, &data, &len );
+	memcpy( clan->master, data, std::min( len, static_cast<decltype(len)>( NAME_LENGTH ) ) );
+	Sql_GetData( sql_handle,  2, &data, &len );
+	memcpy( clan->map, data, std::min( len, static_cast<decltype(len)>( MAP_NAME_LENGTH_EXT ) ) );
 	Sql_GetData(sql_handle,  3, &data, nullptr); clan->max_member = atoi(data);
 
 	clan->connect_member = 0;
@@ -96,16 +99,13 @@ std::shared_ptr<struct clan> inter_clan_fromsql(int clan_id){
 }
 
 int mapif_clan_info( int fd ){
-	int offset;
-	int length;
-
-	length = 4 + clan_db.size() * sizeof( struct clan );
+	size_t offset = 4;
+	size_t length = offset + clan_db.size() * sizeof( struct clan );
 
 	WFIFOHEAD( fd, length );
 	WFIFOW( fd, 0 ) = 0x38A0;
-	WFIFOW( fd, 2 ) = length;
+	WFIFOW( fd, 2 ) = static_cast<int16>( length );
 
-	offset = 4;
 	for( const auto& pair : clan_db ){
 		std::shared_ptr<struct clan> clan = pair.second;
 

+ 5 - 3
src/char/int_guild.cpp

@@ -83,9 +83,11 @@ TIMER_FUNC(guild_save_timer){
 	if( state != 2 ) //Reached the end of the guild db without saving.
 		last_id = 0; //Reset guild saved, return to beginning.
 
-	state = guild_db.size();
-	if( state < 1 ) state = 1; //Calculate the time slot for the next save.
-	add_timer(tick  + (charserv_config.autosave_interval)/state, guild_save_timer, 0, 0);
+	size_t count = std::max( guild_db.size(), static_cast<size_t>( 1 ) );
+
+	// Calculate the time slot for the next save.
+	add_timer( tick + charserv_config.autosave_interval / count, guild_save_timer, 0, 0 );
+
 	return 0;
 }
 

+ 26 - 22
src/char/int_quest.cpp

@@ -23,18 +23,15 @@
  * @return Array of found entries. It has *count entries, and it is care of the
  *         caller to aFree() it afterwards.
  */
-struct quest *mapif_quests_fromsql(uint32 char_id, int *count) {
+struct quest *mapif_quests_fromsql( uint32 char_id, size_t& count ){
 	struct quest *questlog = nullptr;
 	struct quest tmp_quest;
 	SqlStmt *stmt;
 
-	if( !count )
-		return nullptr;
-
 	stmt = SqlStmt_Malloc(sql_handle);
 	if( stmt == nullptr ) {
 		SqlStmt_ShowDebug(stmt);
-		*count = 0;
+		count = 0;
 		return nullptr;
 	}
 
@@ -52,23 +49,27 @@ struct quest *mapif_quests_fromsql(uint32 char_id, int *count) {
 	) {
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_Free(stmt);
-		*count = 0;
+		count = 0;
 		return nullptr;
 	}
 
-	*count = (int)SqlStmt_NumRows(stmt);
-	if( *count > 0 ) {
-		int i = 0;
+	count = static_cast<std::remove_reference<decltype(count)>::type>( SqlStmt_NumRows( stmt ) );
+	if( count > 0 ) {
+		size_t i = 0;
 
-		questlog = (struct quest *)aCalloc(*count, sizeof(struct quest));
+		questlog = (struct quest *)aCalloc( count, sizeof( struct quest ) );
 		while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) {
-			if( i >= *count ) //Sanity check, should never happen
+			// Sanity check, should never happen
+			if( i >= count ){
 				break;
+			}
+
 			memcpy(&questlog[i++], &tmp_quest, sizeof(tmp_quest));
 		}
-		if( i < *count ) {
+
+		if( i < count ) {
 			//Should never happen. Compact array
-			*count = i;
+			count = i;
 			questlog = (struct quest *)aRealloc(questlog, sizeof(struct quest) * i);
 		}
 	}
@@ -136,18 +137,21 @@ bool mapif_quest_update(uint32 char_id, struct quest qd) {
  * @see inter_parse_frommap
  */
 int mapif_parse_quest_save(int fd) {
-	int i, j, k, old_n, new_n = (RFIFOW(fd,2) - 8) / sizeof(struct quest);
 	uint32 char_id = RFIFOL(fd,4);
 	struct quest *old_qd = nullptr, *new_qd = nullptr;
 	bool success = true;
+	size_t old_n, new_n = ( RFIFOW( fd, 2 ) - 8 ) / sizeof( struct quest );
 
 	if( new_n > 0 )
 		new_qd = (struct quest*)RFIFOP(fd,8);
 
-	old_qd = mapif_quests_fromsql(char_id, &old_n);
-	for( i = 0; i < new_n; i++ ) {
+	old_qd = mapif_quests_fromsql( char_id, old_n );
+
+	for( size_t i = 0, j; i < new_n; i++ ) {
 		ARR_FIND(0, old_n, j, new_qd[i].quest_id == old_qd[j].quest_id);
 		if( j < old_n ) { //Update existing quests
+			size_t k;
+
 			//Only states and counts are changable.
 			ARR_FIND(0, MAX_QUEST_OBJECTIVES, k, new_qd[i].count[k] != old_qd[j].count[k]);
 			if( k != MAX_QUEST_OBJECTIVES || new_qd[i].state != old_qd[j].state )
@@ -162,8 +166,10 @@ int mapif_parse_quest_save(int fd) {
 			success &= mapif_quest_add(char_id, new_qd[i]);
 	}
 
-	for( i = 0; i < old_n; i++ ) //Quests not in new_qd but in old_qd are to be erased.
+	// Quests not in new_qd but in old_qd are to be erased.
+	for( size_t i = 0; i < old_n; i++ ){
 		success &= mapif_quest_delete(char_id, old_qd[i].quest_id);
+	}
 
 	if( old_qd )
 		aFree(old_qd);
@@ -189,14 +195,12 @@ int mapif_parse_quest_save(int fd) {
  */
 int mapif_parse_quest_load(int fd) {
 	uint32 char_id = RFIFOL(fd,2);
-	struct quest *tmp_questlog = nullptr;
-	int num_quests;
-
-	tmp_questlog = mapif_quests_fromsql(char_id, &num_quests);
+	size_t num_quests;
+	struct quest* tmp_questlog = mapif_quests_fromsql( char_id, num_quests );
 
 	WFIFOHEAD(fd,num_quests * sizeof(struct quest) + 8);
 	WFIFOW(fd,0) = 0x3860;
-	WFIFOW(fd,2) = num_quests * sizeof(struct quest) + 8;
+	WFIFOW(fd,2) = static_cast<int16>( num_quests * sizeof( struct quest ) + 8 );
 	WFIFOL(fd,4) = char_id;
 
 	if( num_quests > 0 )

+ 10 - 7
src/char/inter.cpp

@@ -709,7 +709,7 @@ int inter_accreg_fromsql(uint32 account_id, uint32 char_id, int fd, int type)
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		Sql_GetData(sql_handle, 1, &data, nullptr);
 
@@ -723,7 +723,7 @@ int inter_accreg_fromsql(uint32 account_id, uint32 char_id, int fd, int type)
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		WFIFOW(fd, 14) += 1;
 
@@ -789,7 +789,7 @@ int inter_accreg_fromsql(uint32 account_id, uint32 char_id, int fd, int type)
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		Sql_GetData(sql_handle, 1, &data, nullptr);
 
@@ -1038,11 +1038,14 @@ void inter_final(void)
  * @param fd
  **/
 void inter_Storage_sendInfo(int fd) {
-	int size = sizeof(struct s_storage_table), len = 4 + interServerDb.size() * size, offset;
+	size_t offset = 4;
+	size_t size = sizeof( struct s_storage_table );
+	size_t len = offset + interServerDb.size() * size;
+
 	// Send storage table information
 	WFIFOHEAD(fd, len);
 	WFIFOW(fd, 0) = 0x388c;
-	WFIFOW(fd, 2) = len;
+	WFIFOW( fd, 2 ) = static_cast<int16>( len );
 	offset = 4;
 	for( auto storage : interServerDb ){
 		memcpy(WFIFOP(fd, offset), storage.second.get(), size);
@@ -1297,7 +1300,7 @@ int mapif_parse_Registry(int fd)
 			size_t lenkey = RFIFOB( fd, cursor );
 			const char* src_key= RFIFOCP(fd, cursor + 1);
 			std::string key( src_key, lenkey );
-			cursor += lenkey + 1;
+			cursor += static_cast<decltype(cursor)>( lenkey + 1 );
 
 			uint32  index = RFIFOL(fd, cursor);
 			cursor += 4;
@@ -1317,7 +1320,7 @@ int mapif_parse_Registry(int fd)
 					size_t len_val = RFIFOB( fd, cursor );
 					const char* src_val= RFIFOCP(fd, cursor + 1);
 					std::string sval( src_val, len_val );
-					cursor += len_val + 1;
+					cursor += static_cast<decltype(cursor)>( len_val + 1 );
 					inter_savereg( account_id, char_id, key.c_str(), index, 0, sval.c_str(), true );
 					break;
 				}

+ 3 - 0
src/common/common-minicore.vcxproj

@@ -144,6 +144,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;_DEBUG;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\zlib\include\;$(SolutionDir)3rdparty\libconfig\</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -163,6 +164,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\zlib\include\;$(SolutionDir)3rdparty\libconfig\</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -184,6 +186,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\zlib\include\;$(SolutionDir)3rdparty\libconfig\</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 3 - 0
src/common/common.vcxproj

@@ -181,6 +181,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;_DEBUG;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -203,6 +204,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -227,6 +229,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 4 - 4
src/common/grfio.cpp

@@ -385,7 +385,7 @@ static void grfio_localpath_create(char* buffer, size_t size, const char* filena
 
 
 /// Reads a file into a newly allocated buffer (from grf or data directory).
-void* grfio_reads(const char* fname, int* size)
+void* grfio_reads(const char* fname, size_t* size)
 {
 	unsigned char* buf2 = nullptr;
 
@@ -457,7 +457,7 @@ void* grfio_reads(const char* fname, int* size)
 }
 
 int32 grfio_read_rsw_water_level( const char* fname ){
-	unsigned char* rsw = (unsigned char *)grfio_read( fname );
+	unsigned char* rsw = (unsigned char *)grfio_reads( fname );
 
 	if( rsw == nullptr ){
 		// Error already reported in grfio_read
@@ -714,7 +714,7 @@ static void grfio_resourcecheck(void)
 {
 	char restable[256];
 	char *buf;
-	int size;
+	size_t size;
 	FILE* fp;
 	int i = 0;
 
@@ -744,7 +744,7 @@ static void grfio_resourcecheck(void)
 		buf[size] = '\0';
 
 		ptr = buf;
-		while( ptr - buf < size )
+		while( static_cast<size_t>( ptr - buf ) < size )
 		{
 			if( grfio_parse_restable_row(ptr) )
 				++i;

+ 1 - 2
src/common/grfio.hpp

@@ -10,9 +10,8 @@ const int32 RSW_NO_WATER = 1000000;
 
 void grfio_init(const char* fname);
 void grfio_final(void);
-void* grfio_reads(const char* fname, int* size);
+void* grfio_reads(const char* fname, size_t* size = nullptr);
 char* grfio_find_file(const char* fname);
-#define grfio_read(fn) grfio_reads(fn, nullptr)
 int32 grfio_read_rsw_water_level( const char* fname );
 
 unsigned long grfio_crc32(const unsigned char *buf, unsigned int len);

+ 4 - 3
src/common/mmo.hpp

@@ -90,7 +90,8 @@ typedef uint32 t_itemid;
 #define MAX_FAME 1000000000 ///Max fame points
 #define MAX_CART 100 ///Maximum item in cart
 #define MAX_SKILL 1623 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit
-#define DEFAULT_WALK_SPEED 150 ///Default walk speed
+#define DEFAULT_WALK_SPEED 150 ///Default walk speed (other than NPC)
+#define DEFAULT_NPC_WALK_SPEED 200 ///Default NPC walk speed
 #define MIN_WALK_SPEED 20 ///Min walk speed
 #define MAX_WALK_SPEED 1000 ///Max walk speed
 #define MAX_STORAGE 600 ///Max number of storage slots a player can have
@@ -1123,8 +1124,8 @@ enum e_pc_reg_loading {
 enum e_party_member_withdraw {
 	PARTY_MEMBER_WITHDRAW_LEAVE,	  ///< /leave
 	PARTY_MEMBER_WITHDRAW_EXPEL,	  ///< Kicked
-	PARTY_MEMBER_WITHDRAW_CANT_LEAVE, ///< TODO: Cannot /leave
-	PARTY_MEMBER_WITHDRAW_CANT_EXPEL, ///< TODO: Cannot be kicked
+	PARTY_MEMBER_WITHDRAW_CANT_LEAVE, ///< Cannot /leave
+	PARTY_MEMBER_WITHDRAW_CANT_EXPEL, ///< Cannot be kicked
 };
 
 enum e_rank {

+ 4 - 4
src/login/account.cpp

@@ -760,7 +760,7 @@ void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 c
 	Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	char* data;
-	int plen = 0;
+	int16 plen = 0;
 	size_t len;
 
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_str_table, account_id) )
@@ -790,7 +790,7 @@ void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 c
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		Sql_GetData(sql_handle, 1, &data, nullptr);
 
@@ -804,7 +804,7 @@ void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 c
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		WFIFOW(fd, 14) += 1;
 
@@ -857,7 +857,7 @@ void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 c
 		plen += 1;
 
 		safestrncpy(WFIFOCP(fd,plen), data, len);
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		Sql_GetData(sql_handle, 1, &data, nullptr);
 

+ 3 - 0
src/login/login-server.vcxproj

@@ -128,6 +128,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;WIN32;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -148,6 +149,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -170,6 +172,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>FD_SETSIZE=4096;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 42 - 24
src/map/atcommand.cpp

@@ -1651,7 +1651,7 @@ ACMD_FUNC(baselevelup)
 		party_send_levelup(sd);
 
 	if( level > 0 && battle_config.atcommand_levelup_events )
-		npc_script_event(sd,NPCE_BASELVUP);
+		npc_script_event( *sd, NPCE_BASELVUP );
 
 	return 0;
 }
@@ -1709,7 +1709,7 @@ ACMD_FUNC(joblevelup)
 	status_calc_pc(sd, SCO_FORCE);
 
 	if( level > 0 && battle_config.atcommand_levelup_events )
-		npc_script_event(sd,NPCE_JOBLVUP);
+		npc_script_event( *sd, NPCE_JOBLVUP );
 
 	return 0;
 }
@@ -3760,16 +3760,12 @@ ACMD_FUNC(lostskill)
  *------------------------------------------*/
 ACMD_FUNC(spiritball)
 {
-	uint32 max_spiritballs;
 	int number;
 	nullpo_retr(-1, sd);
 
-	max_spiritballs = zmin(ARRAYLENGTH(sd->spirit_timer), 0x7FFF);
-
-	if( !message || !*message || (number = atoi(message)) < 0 || number > max_spiritballs )
-	{
+	if( !message || !*message || ( number = atoi( message ) ) < 0 || number > MAX_SPIRITBALL ){
 		char msg[CHAT_SIZE_MAX];
-		safesnprintf(msg, sizeof(msg), msg_txt(sd,1028), max_spiritballs); // Please enter a party name (usage: @party <party_name>).
+		safesnprintf( msg, sizeof( msg ), msg_txt( sd, 1028 ), MAX_SPIRITBALL ); // Please enter an amount (usage: @spiritball <number: 0-%d>).
 		clif_displaymessage(fd, msg);
 		return -1;
 	}
@@ -3819,7 +3815,7 @@ ACMD_FUNC(party)
 		return -1;
 	}
 
-	party_create(sd, party, 0, 0);
+	party_create( *sd, party, 0, 0 );
 
 	return 0;
 }
@@ -3847,7 +3843,7 @@ ACMD_FUNC(guild)
 
 	prev = battle_config.guild_emperium_check;
 	battle_config.guild_emperium_check = 0;
-	guild_create(sd, guild);
+	guild_create( *sd, guild );
 	battle_config.guild_emperium_check = prev;
 
 	return 0;
@@ -3860,8 +3856,9 @@ ACMD_FUNC(breakguild)
 	if (sd->status.guild_id) { // Check if the player has a guild
 		if (sd->guild) { // Check if guild was found
 			if (sd->state.gmaster_flag) { // Check if player is guild master
-				int ret = 0;
-				ret = guild_break(sd, sd->guild->guild.name); // Break guild
+				// Break guild
+				int ret = guild_break( *sd, sd->guild->guild.name );
+
 				if (ret) { // Check if anything went wrong
 					return 0; // Guild was broken
 				} else {
@@ -6171,7 +6168,7 @@ ACMD_FUNC(clearcart)
 #define MAX_SKILLID_PARTIAL_RESULTS 5
 #define MAX_SKILLID_PARTIAL_RESULTS_LEN 74 // "skill " (6) + "%d:" (up to 5) + "%s" (up to 30) + " (%s)" (up to 33)
 ACMD_FUNC(skillid) {
-	int skillen, i, found = 0;
+	int i, found = 0;
 	char partials[MAX_SKILLID_PARTIAL_RESULTS][MAX_SKILLID_PARTIAL_RESULTS_LEN];
 
 	nullpo_retr(-1, sd);
@@ -6181,7 +6178,7 @@ ACMD_FUNC(skillid) {
 		return -1;
 	}
 
-	skillen = strlen(message);
+	size_t skillen = strlen( message );
 
 	for(const auto & skill : skill_db) {
 		uint16 skill_id = skill.second->nameid;
@@ -6415,7 +6412,6 @@ void getring (map_session_data* sd)
 
 	if((flag = pc_additem(sd,&item_tmp,1,LOG_TYPE_COMMAND))) {
 		clif_additem(sd,0,0,flag);
-		map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 	}
 }
 
@@ -6441,6 +6437,28 @@ ACMD_FUNC(marry)
 		return -1;
 	}
 
+	if (!pc_inventoryblank(sd)) {
+		clif_msg_color(sd, MSI_CANT_GET_ITEM_BECAUSE_COUNT, color_table[COLOR_RED]);
+		return -1;
+	}
+
+	if (!pc_inventoryblank(pl_sd)) {
+		clif_msg_color(pl_sd, MSI_CANT_GET_ITEM_BECAUSE_COUNT, color_table[COLOR_RED]);
+		return -1;
+	}
+
+	uint32 w = 0;
+
+	if (w = itemdb_weight((sd->status.sex) ? WEDDING_RING_M : WEDDING_RING_F) && w + sd->weight > sd->max_weight) {
+		clif_msg_color(sd, MSI_CANT_GET_ITEM_BECAUSE_WEIGHT, color_table[COLOR_RED]);
+		return -1;
+	}
+
+	if (w = itemdb_weight((pl_sd->status.sex) ? WEDDING_RING_M : WEDDING_RING_F) && w + pl_sd->weight > pl_sd->max_weight) {
+		clif_msg_color(pl_sd, MSI_CANT_GET_ITEM_BECAUSE_WEIGHT, color_table[COLOR_RED]);
+		return -1;
+	}
+
 	if (pc_marriage(sd, pl_sd)) {
 		clif_displaymessage(fd, msg_txt(sd,1173)); // They are married... wish them well.
 		clif_wedding_effect(&pl_sd->bl); //wedding effect and music [Lupus]
@@ -6538,7 +6556,7 @@ ACMD_FUNC(autotrade) {
 	}
 
 	if (battle_config.at_logout_event)
-		npc_script_event(sd, NPCE_LOGOUT); //Logout Event
+		npc_script_event( *sd, NPCE_LOGOUT );
 
 	channel_pcquit(sd,0xF); //leave all chan
 	clif_authfail_fd(sd->fd, 15);
@@ -8888,7 +8906,7 @@ ACMD_FUNC(showdelay)
  *------------------------------------------*/
 ACMD_FUNC(invite)
 {
-	unsigned int did = sd->duel_group;
+	size_t did = sd->duel_group;
 	map_session_data *target_sd = nullptr;
 
 	memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
@@ -8975,7 +8993,7 @@ ACMD_FUNC(duel)
 			target_sd = map_nick2sd(atcmd_player_name,true);
 
 			if(target_sd != nullptr) {
-				unsigned int newduel;
+				size_t newduel;
 				if((newduel = duel_create(sd, 2)) != -1) {
 					if(target_sd->duel_group > 0 ||	target_sd->duel_invite > 0) {
 						clif_displaymessage(fd, msg_txt(sd,353)); // "Duel: Player already in duel."
@@ -9707,8 +9725,6 @@ static void atcommand_commands_sub(map_session_data* sd, const int fd, AtCommand
 	clif_displaymessage(fd, msg_txt(sd,273)); // "Commands available:"
 
 	for (cmd = (AtCommandInfo*)dbi_first(iter); dbi_exists(iter); cmd = (AtCommandInfo*)dbi_next(iter)) {
-		unsigned int slen = 0;
-
 		switch( type ) {
 			case COMMAND_CHARCOMMAND:
 				if( cmd->char_groups[sd->group->index] == 0 )
@@ -9723,7 +9739,7 @@ static void atcommand_commands_sub(map_session_data* sd, const int fd, AtCommand
 		}
 
 
-		slen = strlen(cmd->command);
+		size_t slen = strlen( cmd->command );
 
 		// flush the text buffer if this command won't fit into it
 		if (slen + cur - line_buff >= CHATBOX_SIZE) {
@@ -9745,7 +9761,8 @@ static void atcommand_commands_sub(map_session_data* sd, const int fd, AtCommand
 		int i, count_bind, gm_lvl = pc_get_group_level(sd);
 		for( i = count_bind = 0; i < atcmd_binding_count; i++ ) {
 			if ( gm_lvl >= ( (type - 1) ? atcmd_binding[i]->level2 : atcmd_binding[i]->level ) ) {
-				unsigned int slen = strlen(atcmd_binding[i]->command);
+				size_t slen = strlen( atcmd_binding[i]->command );
+
 				if ( count_bind == 0 ) {
 					cur = line_buff;
 					memset(line_buff,' ',CHATBOX_SIZE);
@@ -9839,7 +9856,8 @@ ACMD_FUNC(accinfo) {
 */
 ACMD_FUNC(set) {
 	char reg[46], val[128], name[32];
-	int toset = 0, len;
+	int toset = 0;
+	size_t len;
 	uint32 index;
 	bool is_str = false;
 	int64 uid;
@@ -11610,7 +11628,7 @@ bool is_atcommand(const int fd, map_session_data* sd, const char* message, int t
 void atcommand_db_load_groups(){
 	DBIterator *iter = db_iterator(atcommand_db);
 	AtCommandInfo* cmd;
-	int pc_group_max = player_group_db.size();
+	size_t pc_group_max = player_group_db.size();
 
 	for (cmd = (AtCommandInfo*)dbi_first(iter); dbi_exists(iter); cmd = (AtCommandInfo*)dbi_next(iter)) {
 		cmd->at_groups = (char*)aMalloc( pc_group_max * sizeof(char) );

+ 91 - 77
src/map/battle.cpp

@@ -2378,15 +2378,6 @@ static int battle_calc_sizefix(int64 damage, map_session_data *sd, unsigned char
 	return (int)cap_value(damage, INT_MIN, INT_MAX);
 }
 
-static int battle_calc_status_attack(struct status_data *status, short hand)
-{
-	//left-hand penalty on sATK is always 50% [Baalberith]
-	if (hand == EQI_HAND_L)
-		return status->batk;
-	else
-		return 2 * status->batk;
-}
-
 /**
  * Calculates renewal Variance, OverUpgradeBonus, and SizePenaltyMultiplier of weapon damage parts for player
  * @param src Block list of attacker
@@ -3389,10 +3380,12 @@ static bool battle_skill_stacks_masteries_vvs(uint16 skill_id, e_bonus_chk_flag
 		case LG_EARTHDRIVE:
 		case NPC_DRAGONBREATH:
 			return false;
+#ifndef RENEWAL
 		case LK_SPIRALPIERCE:
-			// Spiral Pierce is influenced only by refine bonus and Star Crumbs for players
+			// In Pre-Renewal Spiral Pierce is influenced only by refine bonus and Star Crumbs for players
 			if (chk_flag != BCHK_REFINE && chk_flag != BCHK_STAR)
 				return false;
+#endif
 			break;
 	}
 
@@ -3415,8 +3408,13 @@ static int battle_calc_equip_attack(struct block_list *src, int skill_id)
 		struct status_data *status = status_get_status_data(src);
 		map_session_data *sd = BL_CAST(BL_PC, src);
 
-		if (sd) // add arrow atk if using an applicable skill
-			eatk += (is_skill_using_arrow(src, skill_id) ? sd->bonus.arrow_atk : 0);
+		// Add arrow atk if using an applicable skill
+		if (sd != nullptr && is_skill_using_arrow(src, skill_id)) {
+			int16 ammo_idx = sd->equip_index[EQI_AMMO];
+			// Attack of cannon balls is not added to equip attack, it needs to be added by the skills that use them
+			if (ammo_idx >= 0 && sd->inventory_data[ammo_idx] != nullptr && sd->inventory_data[ammo_idx]->subtype != AMMO_CANNONBALL)
+				eatk += sd->bonus.arrow_atk;
+		}
 
 		return eatk + status->eatk;
 	}
@@ -3898,6 +3896,16 @@ static void battle_calc_attack_masteries(struct Damage* wd, struct block_list *s
 #endif
 				}
 				break;
+#ifdef RENEWAL
+			case GN_CARTCANNON:
+			case NC_ARMSCANNON:
+				// Arrow attack of these skills is not influenced by P.ATK so we add it as mastery attack
+				if (sd != nullptr) {
+					struct status_data* tstatus = status_get_status_data(target);
+					ATK_ADD(wd->masteryAtk, wd->masteryAtk2, battle_attr_fix(src, target, sd->bonus.arrow_atk, sd->bonus.arrow_ele, tstatus->def_ele, tstatus->ele_lv));
+				}
+				break;
+#endif
 		}
 
 		if (sc) { // Status change considered as masteries
@@ -3947,8 +3955,8 @@ static void battle_calc_damage_parts(struct Damage* wd, struct block_list *src,s
 	int right_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_R, false);
 	int left_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_L, false);
 
-	wd->statusAtk += battle_calc_status_attack(sstatus, EQI_HAND_R);
-	wd->statusAtk2 += battle_calc_status_attack(sstatus, EQI_HAND_L);
+	wd->statusAtk += sstatus->batk;
+	wd->statusAtk2 += sstatus->batk;
 
 	if (sd && sd->sc.getSCE(SC_SEVENWIND)) { // Mild Wind applies element to status ATK as well as weapon ATK [helvetica]
 		wd->statusAtk = battle_attr_fix(src, target, wd->statusAtk, right_element, tstatus->def_ele, tstatus->ele_lv);
@@ -3958,6 +3966,9 @@ static void battle_calc_damage_parts(struct Damage* wd, struct block_list *src,s
 		wd->statusAtk2 = battle_attr_fix(src, target, wd->statusAtk2, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv);
 	}
 
+	// Right-hand status attack is doubled after elemental adjustments
+	wd->statusAtk *= 2;
+
 	// Check critical
 	if (wd->type == DMG_MULTI_HIT_CRITICAL || wd->type == DMG_CRITICAL)
 		critical = true;
@@ -4035,26 +4046,34 @@ static void battle_calc_skill_base_damage(struct Damage* wd, struct block_list *
 		case LK_SPIRALPIERCE:
 		case ML_SPIRALPIERCE:
 			if (sd) {
-				short index = sd->equip_index[EQI_HAND_R];
+				battle_calc_damage_parts(wd, src, target, skill_id, skill_lv);
+
+				// Officially statusAtk + weaponAtk + equipAtk make base attack
+				// We simulate this here by adding them all into equip attack
+				ATK_ADD2(wd->equipAtk, wd->equipAtk2, wd->statusAtk + wd->weaponAtk, wd->statusAtk2 + wd->weaponAtk2);
+				// Set statusAtk and weaponAtk to 0
+				ATK_RATE(wd->statusAtk, wd->statusAtk2, 0);
+				ATK_RATE(wd->weaponAtk, wd->weaponAtk2, 0);
 
+				// Add weight
+				short index = sd->equip_index[EQI_HAND_R];
 				if (index >= 0 &&
 					sd->inventory_data[index] &&
 					sd->inventory_data[index]->type == IT_WEAPON)
-					wd->equipAtk += sd->inventory_data[index]->weight*7/100; // weight from spear is treated as equipment ATK on official [helvetica]
+					wd->equipAtk += sd->inventory_data[index]->weight / 10;
 
-				battle_calc_damage_parts(wd, src, target, skill_id, skill_lv);
-				wd->masteryAtk = 0; // weapon mastery is ignored for spiral
-				
-				switch (tstatus->size) { //Size-fix. Is this modified by weapon perfection?
-					case SZ_SMALL: //Small: 115%
-						ATK_RATE(wd->damage, wd->damage2, 115);
-						RE_ALLATK_RATE(wd, 115);
-						break;
-					//case SZ_MEDIUM: //Medium: 100%
-					case SZ_BIG: //Large: 85%
-						ATK_RATE(wd->damage, wd->damage2, 85);
-						RE_ALLATK_RATE(wd, 85);
+				// 70% damage modifier is applied to base attack + weight
+				ATK_RATE(wd->equipAtk, wd->equipAtk2, 70);
+
+				// Additional skill-specific size fix
+				switch (tstatus->size) {
+					case SZ_SMALL: //Small: 130%
+						ATK_RATE(wd->equipAtk, wd->equipAtk2, 130);
 						break;
+					case SZ_MEDIUM: //Medium: 115%
+						ATK_RATE(wd->equipAtk, wd->equipAtk2, 115);
+						break;
+					//case SZ_BIG: //Large: 100%
 				}
 			} else {
 				wd->damage = battle_calc_base_damage(src, sstatus, &sstatus->rhw, sc, tstatus->size, 0); //Monsters have no weight and use ATK instead
@@ -6617,42 +6636,31 @@ static void battle_calc_defense_reduction(struct Damage* wd, struct block_list *
 	}
 
 #ifdef RENEWAL
-	switch(skill_id) {
-		case RK_DRAGONBREATH:
-		case RK_DRAGONBREATH_WATER:
-		case NC_ARMSCANNON:
-		case GN_CARTCANNON:
-		case MO_EXTREMITYFIST:
-			if (attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_R) || attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_L))
-				return;
-			if (is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) || is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L))
-				return;
-			[[fallthrough]];
-		case CR_GRANDCROSS: // Grand Cross is marked as "IgnoreDefense" in renewal as it's applied at the end after already combining ATK and MATK
-		case NPC_GRANDDARKNESS:
-			// Defense reduction by flat value.
-			// This completely bypasses the normal RE DEF Reduction formula.
-			wd->damage -= (def1 + vit_def);
-			if (is_attack_left_handed(src, skill_id))
-				wd->damage2 -= (def1 + vit_def);
-			break;
+	std::bitset<NK_MAX> nk = battle_skill_get_damage_properties(skill_id, wd->miscflag);
+
+	if (nk[NK_SIMPLEDEFENSE]) {
+		// Defense reduction by flat value.
+		// This completely bypasses the normal RE DEF Reduction formula.
+		wd->damage -= (def1 + vit_def);
+		if (is_attack_left_handed(src, skill_id))
+			wd->damage2 -= (def1 + vit_def);
+	}
+	else {
 		/**
 		 * RE DEF Reduction
 		 * Damage = Attack * (4000+eDEF)/(4000+eDEF*10) - sDEF
 		 * Pierce defence gains 1 atk per def/2
 		 */
-		default:
-			if( def1 == -400 ) /* -400 creates a division by 0 and subsequently crashes */
-				def1 = -399;
-			ATK_ADD2(wd->damage, wd->damage2,
-				is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) ? (def1*battle_calc_attack_skill_ratio(wd, src, target, skill_id, skill_lv))/200 : 0,
-				is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L) ? (def1*battle_calc_attack_skill_ratio(wd, src, target, skill_id, skill_lv))/200 : 0
-			);
-			if( !attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_R) && !is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) )
-				wd->damage = wd->damage * (4000+def1) / (4000+10*def1) - vit_def;
-			if( is_attack_left_handed(src, skill_id) && !attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_L) && !is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L) )
-				wd->damage2 = wd->damage2 * (4000+def1) / (4000+10*def1) - vit_def;
-			break;
+		if (def1 == -400) /* -400 creates a division by 0 and subsequently crashes */
+			def1 = -399;
+		ATK_ADD2(wd->damage, wd->damage2,
+			is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R) ? (def1 * battle_calc_attack_skill_ratio(wd, src, target, skill_id, skill_lv)) / 200 : 0,
+			is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L) ? (def1 * battle_calc_attack_skill_ratio(wd, src, target, skill_id, skill_lv)) / 200 : 0
+		);
+		if (!attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_R) && !is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_R))
+			wd->damage = wd->damage * (4000 + def1) / (4000 + 10 * def1) - vit_def;
+		if (is_attack_left_handed(src, skill_id) && !attack_ignores_def(wd, src, target, skill_id, skill_lv, EQI_HAND_L) && !is_attack_piercing(wd, src, target, skill_id, skill_lv, EQI_HAND_L))
+			wd->damage2 = wd->damage2 * (4000 + def1) / (4000 + 10 * def1) - vit_def;
 	}
 #else
 		if (def1 > 100) def1 = 100;
@@ -6783,6 +6791,15 @@ static void battle_calc_attack_plant(struct Damage* wd, struct block_list *src,s
 		return;
 	}
 
+	// Triple Attack has a special property that it does not split damage on plant mode
+	// In pre-renewal, it requires the monster to have exactly 100 def
+	if (skill_id == MO_TRIPLEATTACK && wd->div_ < 0
+#ifndef RENEWAL
+		&& tstatus->def == 100
+#endif
+		)
+		wd->div_ *= -1;
+
 	//For plants we don't continue with the weapon attack code, so we have to apply DAMAGE_DIV_FIX here
 	battle_apply_div_fix(wd, skill_id);
 
@@ -7417,7 +7434,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 			if( is_attack_left_handed( src, skill_id ) ){
 				wd.damage2 = wd.statusAtk2 + wd.weaponAtk2 + wd.equipAtk2 + wd.percentAtk2;
 			}
-			// Apply PATK mod
+			// Apply P.ATK mod
 			// But for Dragonbreaths it only applies if Dragonic Aura is skilled
 			if( ( skill_id != RK_DRAGONBREATH && skill_id != RK_DRAGONBREATH_WATER ) || pc_checkskill( sd, DK_DRAGONIC_AURA ) > 0 ){
 				wd.damage = (int64)floor( (float)( wd.damage * ( 100 + sstatus->patk ) / 100 ) );
@@ -7467,8 +7484,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 
 		// Res reduces physical damage by a percentage and
 		// is calculated before DEF and other reductions.
-		// This should be the official formula. [Rytech]
-		if ((wd.damage + wd.damage2) && tstatus->res > 0 && skill_id != MO_EXTREMITYFIST) {
+		// All skills that use the simple defense formula (damage substracted by DEF+DEF2) ignore Res
+		// TODO: Res formula probably should be: (2000+x)/(2000+5x), but with the reduction rounded down
+		if ((wd.damage + wd.damage2) && tstatus->res > 0 && !nk[NK_SIMPLEDEFENSE]) {
 			short res = tstatus->res;
 			short ignore_res = 0;// Value used as a percentage.
 
@@ -7483,16 +7501,11 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
 					ignore_res += sc->getSCE(SC_POTENT_VENOM)->val2;
 			}
 
-			ignore_res = min(ignore_res, 100);
+			ignore_res = min(ignore_res, battle_config.max_res_mres_ignored);
 
 			if (ignore_res > 0)
 				res -= res * ignore_res / 100;
 
-			// Max damage reduction from Res is officially 50%.
-			// That means 625 Res is needed to hit that cap.
-			if (res > battle_config.max_res_mres_reduction)
-				res = battle_config.max_res_mres_reduction;
-
 			// Apply damage reduction.
 			wd.damage = wd.damage * (5000 + res) / (5000 + 10 * res);
 			wd.damage2 = wd.damage2 * (5000 + res) / (5000 + 10 * res);
@@ -8708,7 +8721,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 						skillratio += 25;
 				}
 #ifdef RENEWAL
-				// SMATK needs to be applied before the skill ratio to prevent rounding issues
+				// S.MATK needs to be applied before the skill ratio to prevent rounding issues
 				if (sd && sstatus->smatk > 0)
 					ad.damage += ad.damage * sstatus->smatk / 100;
 #endif
@@ -8744,7 +8757,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 #ifdef RENEWAL
 		// MRes reduces magical damage by a percentage and
 		// is calculated before MDEF and other reductions.
-		// This should be the official formula. [Rytech]
+		// TODO: MRes formula probably should be: (2000+x)/(2000+5x), but with the reduction rounded down
 		if (ad.damage && tstatus->mres > 0) {
 			short mres = tstatus->mres;
 			short ignore_mres = 0;// Value used as percentage.
@@ -8756,16 +8769,11 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 			if (sc && sc->getSCE(SC_A_VITA))
 				ignore_mres += sc->getSCE(SC_A_VITA)->val2;
 
-			ignore_mres = min(ignore_mres, 100);
+			ignore_mres = min(ignore_mres, battle_config.max_res_mres_ignored);
 
 			if (ignore_mres > 0)
 				mres -= mres * ignore_mres / 100;
 
-			// Max damage reduction from MRes is officially 50%.
-			// That means 625 MRes is needed to hit that cap.
-			if (mres > battle_config.max_res_mres_reduction)
-				mres = battle_config.max_res_mres_reduction;
-
 			// Apply damage reduction.
 			ad.damage = ad.damage * (5000 + mres) / (5000 + 10 * mres);
 		}
@@ -11144,6 +11152,7 @@ static const struct _battle_data {
 	{ "pk_level_range",                     &battle_config.pk_level_range,                  0,      0,      INT_MAX,        },
 	{ "manner_system",                      &battle_config.manner_system,                   0xFFF,  0,      0xFFF,          },
 	{ "pet_equip_required",                 &battle_config.pet_equip_required,              0,      0,      1,              },
+	{ "pet_unequip_destroy",                &battle_config.pet_unequip_destroy,             1,      0,      1,              },
 	{ "multi_level_up",                     &battle_config.multi_level_up,                  0,      0,      1,              },
 	{ "multi_level_up_base",                &battle_config.multi_level_up_base,             0,      0,      MAX_LEVEL,      },
 	{ "multi_level_up_job",                 &battle_config.multi_level_up_job,              0,      0,      MAX_LEVEL,      },
@@ -11441,12 +11450,16 @@ static const struct _battle_data {
 	{ "pet_legacy_formula",                 &battle_config.pet_legacy_formula,              0,      0,      1,              },
 	{ "pet_distance_check",                 &battle_config.pet_distance_check,              5,      0,      50,             },
 	{ "pet_hide_check",                     &battle_config.pet_hide_check,                  1,      0,      1,              },
+	{ "instance_block_leave",               &battle_config.instance_block_leave,            1,      0,      1,              },
+	{ "instance_block_leaderchange",        &battle_config.instance_block_leaderchange,     1,      0,      1,              },
+	{ "instance_block_invite",              &battle_config.instance_block_invite,           1,      0,      1,              },
+	{ "instance_block_expulsion",           &battle_config.instance_block_expulsion,        1,      0,      1,              },
 
 	// 4th Job Stuff
 	{ "use_traitpoint_table",               &battle_config.use_traitpoint_table,            1,      0,      1,              },
 	{ "trait_points_job_change",            &battle_config.trait_points_job_change,         7,      1,      1000,           },
 	{ "max_trait_parameter",                &battle_config.max_trait_parameter,             100,    10,     SHRT_MAX,       },
-	{ "max_res_mres_reduction",             &battle_config.max_res_mres_reduction,          625,    1,      SHRT_MAX,       },
+	{ "max_res_mres_ignored",               &battle_config.max_res_mres_ignored,            50,     0,      100,            },
 	{ "max_ap",                             &battle_config.max_ap,                          200,    100,    1000000000,     },
 	{ "ap_rate",                            &battle_config.ap_rate,                         100,    1,      INT_MAX,        },
 	{ "restart_ap_rate",                    &battle_config.restart_ap_rate,                 0,      0,      100,            },
@@ -11485,6 +11498,7 @@ static const struct _battle_data {
 	{ "feature.instance_allow_reconnect",   &battle_config.instance_allow_reconnect,        0,      0,      1,              },
 #endif
 	{ "synchronize_damage",                 &battle_config.synchronize_damage,              0,      0,      1,              },
+	{ "item_stacking",                      &battle_config.item_stacking,                   1,      0,      1,              },
 
 #include <custom/battle_config_init.inc>
 };

+ 7 - 1
src/map/battle.hpp

@@ -259,6 +259,7 @@ struct Battle_Config
 	int pet_max_atk2; //[Skotlex]
 	int pet_no_gvg; //Disables pets in gvg. [Skotlex]
 	int pet_equip_required;
+	int pet_unequip_destroy;
 	int pet_master_dead;
 
 	int skill_min_damage;
@@ -703,11 +704,15 @@ struct Battle_Config
 	int pet_distance_check;
 	int pet_hide_check;
 
+	int instance_block_leave;
+	int instance_block_leaderchange;
+	int instance_block_invite;
+	int instance_block_expulsion;
 	// 4th Jobs Stuff
 	int trait_points_job_change;
 	int use_traitpoint_table;
 	int max_trait_parameter;
-	int max_res_mres_reduction;
+	int max_res_mres_ignored;
 	int max_ap;
 	int ap_rate;
 	int restart_ap_rate;
@@ -738,6 +743,7 @@ struct Battle_Config
 	int feature_banking_state_enforce;
 	int instance_allow_reconnect;
 	int synchronize_damage;
+	int item_stacking;
 
 #include <custom/battle_config_struct.inc>
 };

+ 1 - 1
src/map/battleground.cpp

@@ -612,7 +612,7 @@ int bg_team_leave(map_session_data *sd, bool quit, bool deserter)
 				sc_start(nullptr, &sd->bl, SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT, 100, 1, static_cast<t_tick>(bg->deserter_time) * 1000); // Deserter timer
 		}
 
-		return bgteam->members.size();
+		return static_cast<int>( bgteam->members.size() );
 	}
 
 	return -1;

+ 3 - 2
src/map/buyingstore.cpp

@@ -248,7 +248,7 @@ int8 buyingstore_create( map_session_data* sd, int zenylimit, unsigned char resu
 		Sql_ShowDebug(mmysql_handle);
 	StringBuf_Destroy(&buf);
 
-	clif_buyingstore_myitemlist(sd);
+	clif_buyingstore_myitemlist( *sd );
 	clif_buyingstore_entry( *sd );
 	idb_put(buyingstore_db, sd->status.char_id, sd);
 
@@ -313,7 +313,7 @@ void buyingstore_open(map_session_data* sd, uint32 account_id)
 	}
 
 	// success
-	clif_buyingstore_itemlist(sd, pl_sd);
+	clif_buyingstore_itemlist( *sd, *pl_sd );
 }
 
 /**
@@ -703,6 +703,7 @@ void do_init_buyingstore_autotrade( void ) {
 
 				// initialize player
 				CREATE(at->sd, map_session_data, 1); // TODO: Dont use Memory Manager allocation anymore and rely on the C++ container
+				new (at->sd) map_session_data();
 				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
 				at->sd->state.autotrade = 1|4;
 				if (battle_config.autotrade_monsterignore)

+ 1 - 1
src/map/cashshop.cpp

@@ -438,7 +438,7 @@ static void cashshop_read_db( void ){
  * @param item_list Array of item ID
  * @return true: success, false: fail
  */
-bool cashshop_buylist( map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ){
+bool cashshop_buylist( map_session_data* sd, uint32 kafrapoints, int n, const PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ){
 	uint32 totalcash = 0;
 	uint32 totalweight = 0;
 	int i,new_;

+ 3 - 1
src/map/cashshop.hpp

@@ -14,12 +14,14 @@
 #include <common/mmo.hpp> // t_itemid
 #include <common/timer.hpp> // ShowWarning, ShowStatus
 
+#include "clif.hpp"
+
 class map_session_data;
 
 void do_init_cashshop( void );
 void do_final_cashshop( void );
 void cashshop_reloaddb( void );
-bool cashshop_buylist( map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list );
+bool cashshop_buylist( map_session_data* sd, uint32 kafrapoints, int n, const PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list );
 
 // Taken from AEGIS (CASH_SHOP_TAB_CODE)
 enum e_cash_shop_tab : uint16{

+ 3 - 2
src/map/chrif.cpp

@@ -1311,7 +1311,8 @@ int chrif_save_scdata(map_session_data *sd) { //parses the sc_data of the player
 	}
 
 	WFIFOW(char_fd,12) = count;
-	WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
+	// Total packet size
+	WFIFOW( char_fd, 2 ) = static_cast<int16>( 14 + count * sizeof( struct status_change_data ) );
 	WFIFOSET(char_fd,WFIFOW(char_fd,2));
 #endif
 	return 0;
@@ -1350,7 +1351,7 @@ int chrif_skillcooldown_save(map_session_data *sd) {
 		return 0;
 
 	WFIFOW(char_fd, 12) = count;
-	WFIFOW(char_fd, 2) = 14 + count * sizeof (struct skill_cooldown_data);
+	WFIFOW( char_fd, 2 ) = static_cast<int16>( 14 + count * sizeof( struct skill_cooldown_data ) );
 	WFIFOSET(char_fd, WFIFOW(char_fd, 2));
 
 	return 0;

+ 63 - 51
src/map/clan.cpp

@@ -11,6 +11,7 @@
 #include <common/nullpo.hpp>
 #include <common/showmsg.hpp>
 
+#include "battle.hpp"
 #include "clif.hpp"
 #include "instance.hpp"
 #include "intif.hpp"
@@ -73,13 +74,12 @@ struct clan* clan_searchname( const char* name ){
 	return c;
 }
 
-map_session_data* clan_getavailablesd( struct clan* clan ){
+map_session_data* clan_getavailablesd( struct clan& clan ){
 	int i;
 
-	nullpo_retr(nullptr, clan);
+	ARR_FIND( 0, clan.max_member, i, clan.members[i] != nullptr );
 
-	ARR_FIND( 0, clan->max_member, i, clan->members[i] != nullptr );
-	return ( i < clan->max_member ) ? clan->members[i] : nullptr;
+	return ( i < clan.max_member ) ? clan.members[i] : nullptr;
 }
 
 int clan_getMemberIndex( struct clan* clan, uint32 account_id ){
@@ -110,111 +110,123 @@ int clan_getNextFreeMemberIndex( struct clan* clan ){
 	}
 }
 
-void clan_member_joined( map_session_data* sd ){
-	nullpo_retv(sd);
-
-	if( sd->clan != nullptr ){
+void clan_member_joined( map_session_data& sd ){
+	if( sd.clan != nullptr ){
 		clif_clan_basicinfo( sd );
-		clif_clan_onlinecount( sd->clan );
+		clif_clan_onlinecount( *sd.clan );
 		return;
 	}
 
-	struct clan* clan = clan_search(sd->status.clan_id);
-	int index;
+	struct clan* clan = clan_search( sd.status.clan_id );
 
-	nullpo_retv(clan);
+	if( clan == nullptr ){
+		return;
+	}
 
-	if( ( index = clan_getNextFreeMemberIndex(clan) ) >= 0 ){
-		sd->clan = clan;
-		clan->members[index] = sd;
+	int index = clan_getNextFreeMemberIndex( clan );
+
+	if( index >= 0 ){
+		sd.clan = clan;
+		clan->members[index] = &sd;
 		clan->connect_member++;
 
 		clif_clan_basicinfo(sd);
 
 		intif_clan_member_joined(clan->id);
-		clif_clan_onlinecount(clan);
+		clif_clan_onlinecount( *clan );
 	}
 }
 
-void clan_member_left( map_session_data* sd ){
-	int index;
-	struct clan* clan;
+void clan_member_left( map_session_data& sd ){
+	struct clan* clan = sd.clan;
+
+	if( clan == nullptr ){
+		return;
+	}
 
-	nullpo_retv(sd);
-	nullpo_retv(clan = sd->clan);
+	int index = clan_getMemberIndex( clan, sd.status.account_id );
 
-	if( ( index = clan_getMemberIndex(clan,sd->status.account_id) ) >= 0 ){
+	if( index >= 0 ){
 		clan->members[index] = nullptr;
 		clan->connect_member--;
 
 		intif_clan_member_left(clan->id);
-		clif_clan_onlinecount(clan);
+		clif_clan_onlinecount( *clan );
 	}
 }
 
-bool clan_member_join( map_session_data *sd, int clan_id, uint32 account_id, uint32 char_id ){
-	struct clan *clan;
+bool clan_member_join( map_session_data& sd, int clan_id, uint32 account_id, uint32 char_id ){
+	struct clan *clan = clan_search( clan_id );
 
-	nullpo_ret(sd);
+	if( clan == nullptr ){
+		return false;
+	}
 
-	if( ( clan = clan_search( clan_id ) ) == nullptr ){
+	if( sd.status.account_id != account_id || sd.status.char_id != char_id || sd.status.clan_id != 0 ){
 		return false;
 	}
 
-	if( sd->status.account_id != account_id || sd->status.char_id != char_id || sd->status.clan_id != 0 ){
+	if( clan->instance_id > 0 && battle_config.instance_block_invite ){
 		return false;
 	}
 
-	sd->status.clan_id = clan->id;
+	sd.status.clan_id = clan->id;
 
 	clan_member_joined(sd);
 
 	return true;
 }
 
-bool clan_member_leave( map_session_data* sd, int clan_id, uint32 account_id, uint32 char_id ){
-	struct clan *clan;
+bool clan_member_leave( map_session_data& sd, int clan_id, uint32 account_id, uint32 char_id ){
+	if( sd.status.account_id != account_id || sd.status.char_id != char_id || sd.status.clan_id != clan_id ){
+		return false;
+	}
 
-	nullpo_ret(sd);
+	struct clan* clan = sd.clan;
 
-	if( sd->status.account_id != account_id || sd->status.char_id != char_id || sd->status.clan_id != clan_id || ( clan = sd->clan ) == nullptr ){
+	if( clan == nullptr ){
+		return false;
+	}
+
+	if( clan->instance_id > 0 && battle_config.instance_block_leave ){
 		return false;
 	}
 
 	clan_member_left(sd);
 
-	sd->clan = nullptr;
-	sd->status.clan_id = 0;
+	sd.clan = nullptr;
+	sd.status.clan_id = 0;
 
 	clif_clan_leave(sd);
 
 	return true;
 }
 
-void clan_recv_message(int clan_id,uint32 account_id,const char *mes,int len) {
-	struct clan *clan;
+void clan_recv_message( int clan_id, uint32 account_id, const char *mes, size_t len ){
+	struct clan *clan = clan_search( clan_id );
 
-	nullpo_retv( clan = clan_search(clan_id) );
+	if( clan == nullptr ){
+		return;
+	}
 
-	clif_clan_message(clan,mes,len);
+	clif_clan_message( *clan, mes, len );
 }
 
-void clan_send_message( map_session_data *sd, const char *mes, int len ){
-	nullpo_retv(sd);
-	nullpo_retv(sd->clan);
+void clan_send_message( map_session_data& sd, const char *mes, size_t len ){
+	if( sd.clan == nullptr ){
+		return;
+	}
 
-	intif_clan_message(sd->status.clan_id,sd->status.account_id,mes,len);
-	clan_recv_message(sd->status.clan_id,sd->status.account_id,mes,len);
-	log_chat( LOG_CHAT_CLAN, sd->status.clan_id, sd->status.char_id, sd->status.account_id, mapindex_id2name( sd->mapindex ), sd->bl.x, sd->bl.y, nullptr, mes );
+	intif_clan_message( sd.status.clan_id, sd.status.account_id, mes, len );
+	clan_recv_message( sd.status.clan_id, sd.status.account_id, mes, len );
+	log_chat( LOG_CHAT_CLAN, sd.status.clan_id, sd.status.char_id, sd.status.account_id, mapindex_id2name( sd.mapindex ), sd.bl.x, sd.bl.y, nullptr, mes );
 }
 
-int clan_get_alliance_count( struct clan *clan, int flag ){
-	int i, count;
-
-	nullpo_ret(clan);
+int clan_get_alliance_count( struct clan& clan, int flag ){
+	int count = 0;
 
-	for( i = 0, count = 0; i < MAX_CLANALLIANCE; i++ ){
-		if(	clan->alliance[i].clan_id > 0 && clan->alliance[i].opposition == flag ){
+	for( int i = 0; i < MAX_CLANALLIANCE; i++ ){
+		if(	clan.alliance[i].clan_id > 0 && clan.alliance[i].opposition == flag ){
 			count++;
 		}
 	}

+ 8 - 8
src/map/clan.hpp

@@ -14,13 +14,13 @@ void do_final_clan();
 struct clan* clan_search( int id );
 struct clan* clan_searchname( const char* name );
 void clan_load_clandata( int count, struct clan* clans );
-void clan_member_joined( map_session_data* sd );
-void clan_member_left( map_session_data* sd );
-bool clan_member_join( map_session_data *sd, int clan_id, uint32 account_id, uint32 char_id );
-bool clan_member_leave( map_session_data* sd, int clan_id, uint32 account_id, uint32 char_id );
-void clan_send_message( map_session_data *sd, const char *mes, int len );
-void clan_recv_message(int clan_id,uint32 account_id,const char *mes,int len);
-map_session_data* clan_getavailablesd( struct clan* clan );
-int clan_get_alliance_count( struct clan *clan, int flag );
+void clan_member_joined( map_session_data& sd );
+void clan_member_left( map_session_data& sd );
+bool clan_member_join( map_session_data& sd, int clan_id, uint32 account_id, uint32 char_id );
+bool clan_member_leave( map_session_data& sd, int clan_id, uint32 account_id, uint32 char_id );
+void clan_send_message( map_session_data& sd, const char *mes, size_t len );
+void clan_recv_message( int clan_id, uint32 account_id, const char *mes, size_t len );
+map_session_data* clan_getavailablesd( struct clan& clan );
+int clan_get_alliance_count( struct clan& clan, int flag );
 
 #endif /* CLAN_HPP */

File diff suppressed because it is too large
+ 248 - 308
src/map/clif.cpp


+ 35 - 30
src/map/clif.hpp

@@ -158,11 +158,12 @@ enum e_party_invite_reply {
 	PARTY_REPLY_ACCEPTED,			    ///< result=2 : "Request for party accepted." -> MsgStringTable[82]
 	PARTY_REPLY_FULL,				    ///< result=3 : "Party Capacity exceeded." -> MsgStringTable[83]
 	PARTY_REPLY_DUAL,				    ///< result=4 : "Character in the same account already joined." -> MsgStringTable[608]
-	PARTY_REPLY_JOINMSG_REFUSE,		    ///< result=5 : !TODO "The character blocked the party invitation." -> MsgStringTable[1324] (since 20070904)
+	PARTY_REPLY_JOINMSG_REFUSE,		    ///< result=5 : "The character blocked the party invitation." -> MsgStringTable[1324] (since 20070904)
 	PARTY_REPLY_UNKNOWN_ERROR,		    ///< result=6 : ??
 	PARTY_REPLY_OFFLINE,			    ///< result=7 : "The Character is not currently online or does not exist." -> MsgStringTable[71] (since 20070904)
 	PARTY_REPLY_INVALID_MAPPROPERTY,    ///< result=8 : !TODO "Unable to organize a party in this map" -> MsgStringTable[1388] (since 20080527)
 	PARTY_REPLY_INVALID_MAPPROPERTY_ME, ///< return=9 : !TODO "Cannot join a party in this map" -> MsgStringTable[1871] (since 20110205)
+	PARTY_REPLY_MEMORIALDUNGEON,	    ///< return=10: "You cannot invite or withdraw while in memorial dungeon" -> MsgStringTable[3027] (since 20161130)
 };
 
 /// Enum for Convex Mirror (SC_BOSSMAPINFO)
@@ -515,6 +516,9 @@ enum clif_messages : uint16_t {
 	// You cannot carry more items because you are overweight.
 	MSI_CANT_GET_ITEM_BECAUSE_WEIGHT = 52,
 
+	// You can't have this item because you will exceed the weight limit.
+	MSI_CANT_GET_ITEM_BECAUSE_COUNT = 220,
+
 	// You can't put this item on.
 	MSI_CAN_NOT_EQUIP_ITEM = 372,
 
@@ -571,7 +575,7 @@ enum clif_messages : uint16_t {
 
 #if (PACKETVER >= 20130807 && PACKETVER <= 20130814) && !defined(PACKETVER_ZERO)
 	// %d seconds left until you can use
-	MSI_ITEM_REUSE_LIMIT_SECOND = 1862,
+	MSI_ITEM_REUSE_LIMIT_SECOND = 1863,
 
 	// Any work in progress (NPC dialog, manufacturing ...) quit and try again.
 	MSI_BUSY = 1924,
@@ -583,13 +587,13 @@ enum clif_messages : uint16_t {
 	MSI_PARTY_MASTER_CHANGE_SAME_MAP = 2095,
 
 	// Merge items available does not exist.
-	MSI_NOT_EXIST_MERGE_ITEM = 2183,
+	MSI_NOT_EXIST_MERGE_ITEM = 2184,
 
 	// This bullet is not suitable for the weapon you are equipping.
 	MSI_WRONG_BULLET = 2494,
 #else
 	// %d seconds left until you can use
-	MSI_ITEM_REUSE_LIMIT_SECOND = 1861,
+	MSI_ITEM_REUSE_LIMIT_SECOND = 1862,
 
 	// Any work in progress (NPC dialog, manufacturing ...) quit and try again.
 	MSI_BUSY = 1923,
@@ -601,7 +605,7 @@ enum clif_messages : uint16_t {
 	MSI_PARTY_MASTER_CHANGE_SAME_MAP = 2094,
 
 	// Merge items available does not exist.
-	MSI_NOT_EXIST_MERGE_ITEM = 2182,
+	MSI_NOT_EXIST_MERGE_ITEM = 2183,
 
 	// This bullet is not suitable for the weapon you are equipping.
 	MSI_WRONG_BULLET = 2493,
@@ -817,7 +821,7 @@ void clif_clearunit_area( block_list& bl, clr_type type );
 void clif_clearunit_delayed(struct block_list* bl, clr_type type, t_tick tick);
 int clif_spawn(struct block_list *bl, bool walking = false);	//area
 void clif_walkok( map_session_data& sd );
-void clif_move(struct unit_data *ud); //area
+void clif_move( struct unit_data& ud ); //area
 void clif_changemap( map_session_data& sd, short m, uint16 x, uint16 y );
 void clif_changemapserver( map_session_data& sd, const char* map, uint16 x, uint16 y, uint32 ip, uint16 port );
 void clif_blown(struct block_list *bl); // area
@@ -826,7 +830,7 @@ void clif_fixpos( block_list& bl );
 void clif_npcbuysell( map_session_data& sd, npc_data& nd );
 void clif_buylist( map_session_data& sd, npc_data& nd );
 void clif_selllist( map_session_data& sd );
-void clif_npc_market_open(map_session_data *sd, struct npc_data *nd);
+void clif_npc_market_open( map_session_data& sd, npc_data& nd );
 void clif_parse_NPCMarketClosed(int fd, map_session_data *sd);
 void clif_parse_NPCMarketPurchase(int fd, map_session_data *sd);
 void clif_scriptmes( map_session_data& sd, uint32 npcid, const char *mes );
@@ -929,8 +933,8 @@ void clif_skill_estimation(map_session_data *sd,struct block_list *dst);
 void clif_skill_warppoint( map_session_data* sd, uint16 skill_id, uint16 skill_lv, const char* map1, const char* map2 = "", const char* map3 = "", const char* map4 = "" );
 void clif_skill_memomessage( map_session_data& sd, e_ack_remember_warppoint_result result );
 void clif_skill_teleportmessage( map_session_data& sd, e_notify_mapinfo_result result );
-void clif_skill_produce_mix_list(map_session_data *sd, int skill_id, int trigger);
-void clif_cooking_list(map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type);
+void clif_skill_produce_mix_list( map_session_data& sd, int skill_id, int trigger );
+void clif_cooking_list( map_session_data& sd, int trigger, uint16 skill_id, int qty, int list_type );
 
 void clif_produceeffect(map_session_data* sd,int flag, t_itemid nameid);
 
@@ -996,9 +1000,9 @@ void clif_changed_dir(struct block_list *bl, enum send_target target);
 void clif_openvendingreq( map_session_data& sd, uint16 num );
 void clif_showvendingboard( map_session_data& sd, enum send_target target = AREA_WOS, struct block_list* tbl = nullptr );
 void clif_closevendingboard(struct block_list* bl, int fd);
-void clif_vendinglist( map_session_data* sd, map_session_data* vsd );
+void clif_vendinglist( map_session_data& sd, map_session_data& vsd );
 void clif_buyvending( map_session_data& sd, uint16 index, uint16 amount, e_pc_purchase_result_frommc result );
-void clif_openvending(map_session_data* sd, int id, struct s_vending* vending);
+void clif_openvending( map_session_data& sd );
 void clif_vendingreport( map_session_data& sd, uint16 index, uint16 amount, uint32 char_id, int32 zeny );
 
 void clif_movetoattack( map_session_data& sd, block_list& bl );
@@ -1020,35 +1024,34 @@ void clif_party_job_and_level( map_session_data& sd );
 void clif_party_dead( map_session_data& sd );
 
 // guild
-void clif_guild_created(map_session_data *sd,int flag);
+void clif_guild_created( map_session_data& sd, int flag );
 void clif_guild_belonginfo( map_session_data& sd );
 void clif_guild_masterormember(map_session_data *sd);
 void clif_guild_basicinfo( map_session_data& sd );
 void clif_guild_allianceinfo(map_session_data *sd);
 void clif_guild_memberlist( map_session_data& sd );
-void clif_guild_skillinfo(map_session_data* sd);
+void clif_guild_skillinfo( map_session_data& sd );
 void clif_guild_send_onlineinfo(map_session_data *sd); //[LuzZza]
 void clif_guild_memberlogin_notice(const struct mmo_guild &g,int idx,int flag);
-void clif_guild_invite(const map_session_data &sd, const struct mmo_guild &g);
-void clif_guild_inviteack(map_session_data *sd,int flag);
-void clif_guild_leave(map_session_data *sd,const char *name,const char *mes);
-void clif_guild_expulsion(map_session_data* sd, const char* name, const char* mes, uint32 account_id);
+void clif_guild_invite( map_session_data& sd, const struct mmo_guild& g );
+void clif_guild_inviteack( map_session_data& sd, int flag );
+void clif_guild_leave( map_session_data& sd, const char* name, uint32 char_id, const char* mes );
+void clif_guild_expulsion( map_session_data& sd, const char* name, uint32 char_id, const char* mes );
 void clif_guild_positionchanged(const struct mmo_guild &g,int idx);
 void clif_guild_memberpositionchanged(const struct mmo_guild &g,int idx);
 void clif_guild_emblem(const map_session_data &sd, const struct mmo_guild &g);
 void clif_guild_emblem_area(struct block_list* bl);
-void clif_guild_notice(map_session_data* sd);
-void clif_guild_message(const struct mmo_guild &g,uint32 account_id,const char *mes,int len);
+void clif_guild_notice( map_session_data& sd );
+void clif_guild_message( const struct mmo_guild& g, uint32 account_id, const char* mes, size_t len );
 void clif_guild_reqalliance(map_session_data *sd,uint32 account_id,const char *name);
 void clif_guild_allianceack(map_session_data *sd,int flag);
 void clif_guild_delalliance(map_session_data *sd,int guild_id,int flag);
 void clif_guild_oppositionack(map_session_data *sd,int flag);
-void clif_guild_broken(map_session_data *sd,int flag);
+void clif_guild_broken( map_session_data& sd, int flag );
 void clif_guild_xy( map_session_data& sd );
 void clif_guild_xy_single( map_session_data& sd, map_session_data& tsd );
 void clif_guild_xy_remove( map_session_data& sd );
 void clif_guild_castle_list(map_session_data& sd);
-void clif_guild_castle_info(map_session_data& sd, std::shared_ptr<guild_castle> castle );
 void clif_guild_castle_teleport_res(map_session_data& sd, enum e_siege_teleport_result result);
 
 // Battleground
@@ -1069,7 +1072,7 @@ void clif_bg_queue_lobby_notify(const char *name, map_session_data *sd);
 void clif_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, map_session_data *sd);
 
 // Instancing
-void clif_instance_create(int instance_id, int num);
+void clif_instance_create( int instance_id, size_t num );
 void clif_instance_changewait(int instance_id, int num);
 void clif_instance_status(int instance_id, unsigned int limit1, unsigned int limit2);
 void clif_instance_changestatus(int instance_id, e_instance_notify type, unsigned int limit);
@@ -1241,17 +1244,17 @@ void clif_showdigit(map_session_data* sd, unsigned char type, int value);
 /// Buying Store System
 void clif_buyingstore_open(map_session_data* sd);
 void clif_buyingstore_open_failed(map_session_data* sd, unsigned short result, unsigned int weight);
-void clif_buyingstore_myitemlist(map_session_data* sd);
+void clif_buyingstore_myitemlist( map_session_data& sd );
 void clif_buyingstore_entry( map_session_data& sd, struct block_list* tbl = nullptr );
 void clif_buyingstore_disappear_entry( map_session_data& sd, struct block_list* tbl = nullptr );
-void clif_buyingstore_itemlist(map_session_data* sd, map_session_data* pl_sd);
+void clif_buyingstore_itemlist( map_session_data& sd, map_session_data& pl_sd );
 void clif_buyingstore_trade_failed_buyer(map_session_data* sd, short result);
 void clif_buyingstore_update_item(map_session_data* sd, t_itemid nameid, unsigned short amount, uint32 char_id, int zeny);
 void clif_buyingstore_delete_item(map_session_data* sd, short index, unsigned short amount, int price);
 void clif_buyingstore_trade_failed_seller(map_session_data* sd, short result, t_itemid nameid);
 
 /// Search Store System
-void clif_search_store_info_ack(map_session_data* sd);
+void clif_search_store_info_ack( map_session_data& sd );
 void clif_search_store_info_failed(map_session_data* sd, unsigned char reason);
 void clif_open_search_store_info(map_session_data* sd);
 void clif_search_store_info_click_ack(map_session_data* sd, short x, short y);
@@ -1290,10 +1293,10 @@ void clif_snap( struct block_list *bl, short x, short y );
 void clif_monster_hp_bar( struct mob_data* md, int fd );
 
 // Clan System
-void clif_clan_basicinfo( map_session_data *sd );
-void clif_clan_message(struct clan *clan,const char *mes,int len);
-void clif_clan_onlinecount( struct clan* clan );
-void clif_clan_leave( map_session_data* sd );
+void clif_clan_basicinfo( map_session_data& sd );
+void clif_clan_message( struct clan &clan, const char *mes, size_t len );
+void clif_clan_onlinecount( struct clan& clan );
+void clif_clan_leave( map_session_data& sd );
 
 // Bargain Tool
 void clif_sale_start(struct sale_item_data* sale_item, struct block_list* bl, enum send_target target);
@@ -1371,7 +1374,7 @@ void clif_attendence_response( map_session_data *sd, int32 data );
 
 void clif_weight_limit( map_session_data* sd );
 
-void clif_guild_storage_log( map_session_data* sd, std::vector<struct guild_log_entry>& log, enum e_guild_storage_log result );
+void clif_guild_storage_log( map_session_data& sd, std::vector<struct guild_log_entry>& log, enum e_guild_storage_log result );
 
 void clif_camerainfo( map_session_data* sd, bool show, float range = 0.0f, float rotation = 0.0f, float latitude = 0.0f );
 
@@ -1437,4 +1440,6 @@ void clif_set_npc_window_size(map_session_data& sd, int width, int height);
 void clif_set_npc_window_pos(map_session_data& sd, int x, int y);
 void clif_set_npc_window_pos_percent(map_session_data& sd, int x, int y);
 
+void clif_noask_sub( map_session_data& sd, map_session_data& tsd, int type );
+
 #endif /* CLIF_HPP */

+ 12 - 38
src/map/clif_packetdb.hpp

@@ -118,13 +118,13 @@
 	parseable_packet(0x00f7,2,clif_parse_CloseKafra,0);
 	parseable_packet(0x00f9,26,clif_parse_CreateParty,2);
 	packet(0x00fb,-1);
-	parseable_packet(0x00fc,6,clif_parse_PartyInvite,2);
+	parseable_packet( HEADER_CZ_REQ_JOIN_GROUP, sizeof( PACKET_CZ_REQ_JOIN_GROUP ), clif_parse_PartyInvite, 0 );
 	packet(0x00fd,27);
-	parseable_packet(0x00ff,10,clif_parse_ReplyPartyInvite,2,6);
-	parseable_packet(0x0100,2,clif_parse_LeaveParty,0);
+	parseable_packet( HEADER_CZ_JOIN_GROUP, sizeof( PACKET_CZ_JOIN_GROUP ), clif_parse_ReplyPartyInvite, 0 );
+	parseable_packet( HEADER_CZ_REQ_LEAVE_GROUP, sizeof( PACKET_CZ_REQ_LEAVE_GROUP ), clif_parse_LeaveParty, 0 );
 	packet(0x0101,6);
 	parseable_packet(0x0102,6,clif_parse_PartyChangeOption,2);
-	parseable_packet(0x0103,30,clif_parse_RemovePartyMember,2,6);
+	parseable_packet( HEADER_CZ_REQ_EXPEL_GROUP_MEMBER, sizeof( PACKET_CZ_REQ_EXPEL_GROUP_MEMBER ), clif_parse_RemovePartyMember, 0 );
 	packet(0x0104,79);
 	parseable_packet(0x0108,-1,clif_parse_PartyMessage,2,4);
 	packet(0x0109,-1);
@@ -185,29 +185,21 @@
 	packet(0x0156,-1);
 	packet(0x0157,6);
 	packet(0x0158,-1);
-	parseable_packet(0x0159,54,clif_parse_GuildLeave,2,6,10,14);
-	packet(0x015a,66);
-	parseable_packet(0x015b,54,clif_parse_GuildExpulsion,2,6,10,14);
-	packet(0x015c,90);
-	parseable_packet(0x015d,42,clif_parse_GuildBreak,2);
-	packet(0x015e,6);
+	parseable_packet( HEADER_CZ_REQ_LEAVE_GUILD, sizeof( PACKET_CZ_REQ_LEAVE_GUILD ), clif_parse_GuildLeave, 0 );
+	parseable_packet( HEADER_CZ_REQ_BAN_GUILD, sizeof( PACKET_CZ_REQ_BAN_GUILD ), clif_parse_GuildExpulsion, 0 );
+	parseable_packet( HEADER_CZ_REQ_DISORGANIZE_GUILD, sizeof( PACKET_CZ_REQ_DISORGANIZE_GUILD ), clif_parse_GuildBreak, 0 );
 	packet(0x015f,42);
 	packet(0x0160,-1);
 	parseable_packet(0x0161,-1,clif_parse_GuildChangePositionInfo,2,4);
-	packet(0x0162,-1);
 	packet(0x0163,-1);
 	packet(0x0164,-1);
 	parseable_packet(0x0165,30,clif_parse_CreateGuild,2,6);
 	packet(0x0166,-1);
-	packet(0x0167,3);
-	parseable_packet(0x0168,14,clif_parse_GuildInvite,2,6,10);
-	packet(0x0169,3);
-	packet(0x016a,30);
-	parseable_packet(0x016b,10,clif_parse_GuildReplyInvite,2,6);
+	parseable_packet( HEADER_CZ_REQ_JOIN_GUILD, sizeof( PACKET_CZ_REQ_JOIN_GUILD ), clif_parse_GuildInvite, 0 );
+	parseable_packet( HEADER_CZ_JOIN_GUILD, sizeof( PACKET_CZ_JOIN_GUILD ), clif_parse_GuildReplyInvite, 0 );
 	packet(0x016c,43);
 	packet(0x016d,14);
 	parseable_packet(0x016e,186,clif_parse_GuildChangeNotice,2,6,66);
-	packet(0x016f,182);
 	parseable_packet(0x0170,14,clif_parse_GuildRequestAlliance,2,6,10);
 	packet(0x0171,30);
 	parseable_packet(0x0172,10,clif_parse_GuildReplyAlliance,2,6);
@@ -233,7 +225,6 @@
 	parseable_packet(0x018a,4,clif_parse_QuitGame,2);
 	packet(0x018b,4);
 	packet(0x018c,29);
-	packet(0x018d,-1);
 	parseable_packet( HEADER_CZ_REQMAKINGITEM, sizeof( struct PACKET_CZ_REQMAKINGITEM ), clif_parse_ProduceMix, 0 );
 	packet( HEADER_ZC_ACK_REQMAKINGITEM, sizeof( PACKET_ZC_ACK_REQMAKINGITEM ) );
 	parseable_packet(0x0190,90,clif_parse_UseSkillToPosMoreInfo,2,4,6,8,10);
@@ -267,7 +258,6 @@
 	packet(0x01b0,11);
 	packet(0x01b1,7);
 	parseable_packet(0x01b2,-1,clif_parse_OpenVending,2,4,84,85);
-	packet(0x01b4,12);
 	packet(0x01b5,18);
 	packet(0x01b6,114);
 	packet(0x01b7,6);
@@ -778,7 +768,6 @@
 // 2005-10-10aSakexe
 #if PACKETVER >= 20051010
 	packet(0x020e,32);
-	packet(0x025a,-1);
 	parseable_packet( HEADER_CZ_REQ_MAKINGITEM, sizeof( struct PACKET_CZ_REQ_MAKINGITEM ), clif_parse_Cooking, 0 );
 #endif
 
@@ -996,9 +985,9 @@
 	packet(0x02c0,2);
 	packet(0x02c1,-1);
 	packet(0x02c2,-1);
-	parseable_packet(0x02c4,26,clif_parse_PartyInvite2,2);
+	parseable_packet( HEADER_CZ_PARTY_JOIN_REQ, sizeof( PACKET_CZ_PARTY_JOIN_REQ ), clif_parse_PartyInvite2, 0 );
 	packet(0x02c5,30);
-	parseable_packet(0x02c7,7,clif_parse_ReplyPartyInvite2,2,6);
+	parseable_packet( HEADER_CZ_PARTY_JOIN_REQ_ACK, sizeof( PACKET_CZ_PARTY_JOIN_REQ_ACK ), clif_parse_ReplyPartyInvite2, 0 );
 	parseable_packet( HEADER_CZ_PARTY_CONFIG, sizeof( PACKET_CZ_PARTY_CONFIG ), clif_parse_PartyTick, 0 );
 	packet(0x02ca,3);
 	packet(0x02cb,20);
@@ -1542,18 +1531,14 @@
 	packet(0x0810,3);
 	parseable_packet(0x0811,-1,clif_parse_ReqOpenBuyingStore,2,4,8,9,89);
 	//packet(0x0812,86);
-	//packet(0x0813,6);
 	//packet(0x0815,-1);
 	//packet(0x0817,-1);
-	//packet(0x0818,6);
 	//packet(0x0819,4);
 #endif
 
 // 2010-03-09aRagexeRE
 #if PACKETVER >= 20100309
-	packet(0x0813,-1);
 	//packet(0x0815,6);
-	packet(0x0818,-1);
 	//packet(0x0819,10);
 	//packet(0x081A,4);
 	//packet(0x081B,4);
@@ -1655,7 +1640,6 @@
 
 // 2010-08-03aRagexeRE
 #if PACKETVER >= 20100803
-	packet(0x0839,66);
 	parseable_packet(0x0842,6,clif_parse_GMRecall2,2);
 	parseable_packet(0x0843,6,clif_parse_GMRemove2,2);
 #endif
@@ -1827,7 +1811,7 @@
 	parseable_packet(0x090A,26,clif_parse_bg_queue_request_queue_number,2);
 	packet( HEADER_ZC_ENTRY_QUEUE_INIT , sizeof(PACKET_ZC_ENTRY_QUEUE_INIT) );
 	packet(0x0977,14); //Monster HP Bar
-	parseable_packet(0x0916,26,clif_parse_GuildInvite2,2);
+	parseable_packet( HEADER_CZ_REQ_JOIN_GUILD2, sizeof( PACKET_CZ_REQ_JOIN_GUILD2 ), clif_parse_GuildInvite2, 0 );
 	parseable_packet(0x091d,41,clif_parse_PartyBookingRegisterReq,2,4,6);
 	// Merge Item
 	parseable_packet( HEADER_CZ_REQ_MERGE_ITEM, -1, clif_parse_merge_item_req, 0 );
@@ -1985,16 +1969,11 @@
 	parseable_packet(0x09CE,102,clif_parse_GM_Item_Monster,2);
 	parseable_packet(0x09D4,2,clif_parse_NPCShopClosed,0);
 	//NPC Market
-	packet(0x09D5,-1);
 	parseable_packet( HEADER_CZ_NPC_MARKET_PURCHASE, -1, clif_parse_NPCMarketPurchase, 0 );
 	packet(0x09D7,-1);
 	parseable_packet(0x09D8,2,clif_parse_NPCMarketClosed,0);
 	// Clan System
-	packet(0x0988,6);
-	packet(0x0989,2);
-	packet(0x098A,-1);
 	parseable_packet(0x098D,-1,clif_parse_clan_chat,2,4);
-	packet(0x098E,-1);
 	// Sale
 	parseable_packet( HEADER_CZ_REQ_CASH_BARGAIN_SALE_ITEM_INFO, -1, clif_parse_sale_search, 0 );
 	packet( HEADER_ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO, sizeof( PACKET_ZC_ACK_CASH_BARGAIN_SALE_ITEM_INFO ) );
@@ -2015,11 +1994,6 @@
 	packet(0x09DD,-1); // ZC_NOTIFY_STANDENTRY10
 #endif
 
-// 2014-02-05bRagexeRE
-#if PACKETVER >= 20140205
-	packet(0x09DA,-1);
-#endif
-
 // 2014-10-08Ragexe
 #if PACKETVER >= 20141008
 	parseable_packet(0x9FB, -1, clif_parse_pet_evolution, 2, 4); // CZ_PET_EVOLUTION

+ 271 - 164
src/map/guild.cpp

@@ -13,6 +13,7 @@
 #include <common/mapindex.hpp>
 #include <common/nullpo.hpp>
 #include <common/showmsg.hpp>
+#include <common/socket.hpp> // session_isActive
 #include <common/strlib.hpp>
 #include <common/timer.hpp>
 #include <common/utilities.hpp>
@@ -601,22 +602,19 @@ int guild_getposition(const map_session_data& sd) {
 }
 
 //Creation of member information
-void guild_makemember(struct guild_member *m,map_session_data *sd) {
-	nullpo_retv(sd);
-
-	memset(m,0,sizeof(struct guild_member));
-	m->account_id	= sd->status.account_id;
-	m->char_id		= sd->status.char_id;
-	m->hair			= sd->status.hair;
-	m->hair_color	= sd->status.hair_color;
-	m->gender		= sd->status.sex;
-	m->class_		= sd->status.class_;
-	m->lv			= sd->status.base_level;
-	m->exp			= 0;
-	m->online		= 1;
-	m->position		= MAX_GUILDPOSITION-1;
-	safestrncpy(m->name,sd->status.name,NAME_LENGTH);
-	m->last_login	= (uint32)time(nullptr);
+void guild_makemember( struct guild_member& m, map_session_data& sd ){
+	m.account_id = sd.status.account_id;
+	m.char_id = sd.status.char_id;
+	m.hair = sd.status.hair;
+	m.hair_color = sd.status.hair_color;
+	m.gender = sd.status.sex;
+	m.class_ = sd.status.class_;
+	m.lv = sd.status.base_level;
+	m.exp = 0;
+	m.online = 1;
+	m.position = MAX_GUILDPOSITION - 1;
+	safestrncpy( m.name, sd.status.name, NAME_LENGTH );
+	m.last_login = static_cast<decltype(m.last_login)>( time( nullptr ) );
 }
 
 /**
@@ -688,32 +686,36 @@ int guild_send_dot_remove(map_session_data *sd) {
 }
 //------------------------------------------------------------------------
 
-int guild_create(map_session_data *sd, const char *name) {
+bool guild_create( map_session_data& sd, const char* name ){
 	char tname[NAME_LENGTH];
-	struct guild_member m;
-	nullpo_ret(sd);
 
 	safestrncpy(tname, name, NAME_LENGTH);
 	trim(tname);
 
-	if( !tname[0] )
-		return 0; // empty name
+	// empty name
+	if( !tname[0] ){
+		return false;
+	}
 
-	if( sd->status.guild_id ) {
+	if( sd.status.guild_id ) {
 		// already in a guild
-		clif_guild_created(sd,1);
-		return 0;
+		clif_guild_created( sd, 1 );
+		return false;
 	}
-	if( battle_config.guild_emperium_check && pc_search_inventory(sd,ITEMID_EMPERIUM) == -1 ) {
+
+	if( battle_config.guild_emperium_check && pc_search_inventory( &sd, ITEMID_EMPERIUM ) == -1 ){
 		// item required
-		clif_guild_created(sd,3);
-		return 0;
+		clif_guild_created( sd, 3 );
+		return false;
 	}
 
-	guild_makemember(&m,sd);
+	struct guild_member m = {};
+
+	guild_makemember( m, sd );
 	m.position=0;
 	intif_guild_create(name,&m);
-	return 1;
+
+	return true;
 }
 
 //Whether or not to create guild
@@ -723,12 +725,13 @@ int guild_created(uint32 account_id,int guild_id) {
 	if(sd==nullptr)
 		return 0;
 	if(!guild_id) {
-		clif_guild_created(sd, 2); // Creation failure (presence of the same name Guild)
+		clif_guild_created( *sd, 2 ); // Creation failure (presence of the same name Guild)
 		return 0;
 	}
 
 	sd->status.guild_id = guild_id;
-	clif_guild_created(sd,0);
+	clif_guild_created( *sd, 0 );
+
 	if(battle_config.guild_emperium_check){
 		int index = pc_search_inventory(sd,ITEMID_EMPERIUM);
 
@@ -769,13 +772,10 @@ int guild_npc_request_info(int guild_id,const char *event) {
  * Close trade window if party member is kicked when trade a party bound item
  * @param sd
  **/
-static void guild_trade_bound_cancel(map_session_data *sd) {
+static void guild_trade_bound_cancel( map_session_data& sd ){
 #ifdef BOUND_ITEMS
-	nullpo_retv(sd);
-	if (sd->state.isBoundTrading&(1<<BOUND_GUILD))
-		trade_tradecancel(sd);
-#else
-	;
+	if( sd.state.isBoundTrading&(1<<BOUND_GUILD))
+		trade_tradecancel( &sd );
 #endif
 }
 
@@ -890,11 +890,11 @@ int guild_recv_info(const struct mmo_guild &sg) {
 		}
 
 		if (before.skill_point != g->guild.skill_point)
-			clif_guild_skillinfo(sd); //Submit information skills
+			clif_guild_skillinfo( *sd ); // Submit information skills
 
 		if (guild_new) { // Send information and affiliation if unsent
 			clif_guild_belonginfo( *sd );
-			clif_guild_notice(sd);
+			clif_guild_notice( *sd );
 			sd->guild_emblem_id = g->guild.emblem_id;
 		}
 		if (g->instance_id > 0)
@@ -918,102 +918,148 @@ int guild_recv_info(const struct mmo_guild &sg) {
 /*=============================================
  * Player sd send a guild invatation to player tsd to join his guild
  *--------------------------------------------*/
-int guild_invite(map_session_data *sd, map_session_data *tsd) {
-	int i;
+bool guild_invite( map_session_data& sd, map_session_data* tsd ){
+	// No nullpo_retr, because its valid that target players might not exist or are not online
+	if( tsd == nullptr ){
+		return false;
+	}
 
-	nullpo_ret(sd);
+	auto& g = sd.guild;
 
-	auto &g = sd->guild;
+	if( g == nullptr ){
+		return false;
+	}
 
-	if(tsd==nullptr || g==nullptr)
-		return 0;
+	if( g->instance_id && battle_config.instance_block_invite ){
+		return false;
+	}
 
-	if( (i=guild_getposition(*sd))<0 || !(g->guild.position[i].mode&GUILD_PERM_INVITE) )
-		return 0; //Invite permission.
+	// Guild locked.
+	if( map_getmapflag( sd.bl.m, MF_GUILDLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 228 ) ); // Guild modification is disabled on this map.
+		return false;
+	}
 
-	if(!battle_config.invite_request_check) {
-	if (tsd->party_invite > 0 || tsd->trade_partner || tsd->adopt_invite) { //checking if there no other invitation pending
-			clif_guild_inviteack(sd,0);
-			return 0;
-		}
+	// @noask [LuzZza]
+	if( tsd->state.noask ){
+		clif_noask_sub( sd, *tsd, 395 ); // Autorejected guild invite from %s.
+		return false;
 	}
 
-	if (!tsd->fd) { //You can't invite someone who has already disconnected.
+	// Players in a clan can not join a guild
+	if( tsd->clan ){
+		// TODO: message?
+		return false;
+	}
+
+	// Invite permission.
+	if( !guild_has_permission( sd, GUILD_PERM_INVITE ) ){
+		// TODO: message?
+		return false;
+	}
+
+	// Checking if there no other invitation pending
+	if( !battle_config.invite_request_check && ( tsd->party_invite > 0 || tsd->trade_partner || tsd->adopt_invite ) ){
+		clif_guild_inviteack( sd, 0 );
+		return false;
+	}
+
+	// You can't invite someone who has already disconnected.
+	if( !session_isActive( tsd->fd ) ){
 		clif_guild_inviteack(sd,1);
-		return 0;
+		return false;
 	}
 
-	if(tsd->status.guild_id>0 ||
-		tsd->guild_invite>0 ||
-		map_flag_gvg2(tsd->bl.m))
-	{	//Can't invite people inside castles. [Skotlex]
+	// Can't invite people inside castles. [Skotlex]
+	if( tsd->status.guild_id > 0 || tsd->guild_invite > 0 || map_flag_gvg2( tsd->bl.m ) ){
 		clif_guild_inviteack(sd,0);
-		return 0;
+		return false;
 	}
 
+	int i;
+
 	//search an empty spot in guild
 	ARR_FIND( 0, g->guild.max_member, i, g->guild.member[i].account_id == 0 );
-	if(i==g->guild.max_member){
+	if( i == g->guild.max_member ){
 		clif_guild_inviteack(sd,3);
-		return 0;
+		return false;
 	}
 
-	tsd->guild_invite=sd->status.guild_id;
-	tsd->guild_invite_account=sd->status.account_id;
+	tsd->guild_invite = sd.status.guild_id;
+	tsd->guild_invite_account = sd.status.account_id;
 
 	clif_guild_invite(*tsd, g->guild);
-	return 0;
+
+	return true;
 }
 
 /// Guild invitation reply.
 /// flag: 0:rejected, 1:accepted
-int guild_reply_invite(map_session_data* sd, int guild_id, int flag) {
-	map_session_data* tsd;
-
-	nullpo_ret(sd);
-
+bool guild_reply_invite( map_session_data& sd, int guild_id, int flag ){
 	// subsequent requests may override the value
-	if( sd->guild_invite != guild_id )
-		return 0; // mismatch
+	if( sd.guild_invite != guild_id ){
+		return false; // mismatch
+	}
 
 	// look up the person who sent the invite
 	//NOTE: this can be nullptr because the person might have logged off in the meantime
-	tsd = map_id2sd(sd->guild_invite_account);
+	map_session_data* tsd = map_id2sd( sd.guild_invite_account );
 
-	if ( sd->status.guild_id > 0 ) {
-	// [Paradox924X]
-	 // Already in another guild.
-		if ( tsd ) clif_guild_inviteack(tsd,0);
-		return 0;
-	} else if( flag == 0 ) {// rejected
-		sd->guild_invite = 0;
-		sd->guild_invite_account = 0;
-		if( tsd ) clif_guild_inviteack(tsd,1);
-	} else {// accepted
-		struct guild_member m;
-		auto g = guild_search(guild_id);
-		int i;
+	// Already in another guild.
+	if( sd.status.guild_id > 0 ){
+		// Set the flag to rejected, no matter what
+		flag = 0;
+	}
 
-		if (!g) {
-			sd->guild_invite = 0;
-			sd->guild_invite_account = 0;
-			return 0;
+	// rejected
+	if( flag == 0 ){
+		sd.guild_invite = 0;
+		sd.guild_invite_account = 0;
+
+		if( tsd != nullptr ){
+			clif_guild_inviteack( *tsd, 1 );
 		}
 
-		ARR_FIND( 0, g->guild.max_member, i, g->guild.member[i].account_id == 0 );
-		if( i == g->guild.max_member ) {
-			sd->guild_invite = 0;
-			sd->guild_invite_account = 0;
-			if( tsd ) clif_guild_inviteack(tsd,3);
-			return 0;
+		return true;
+	}
+
+	// accepted
+	auto g = guild_search( guild_id );
+
+	if( g == nullptr ){
+		sd.guild_invite = 0;
+		sd.guild_invite_account = 0;
+		return false;
+	}
+
+	if( g->instance_id && battle_config.instance_block_invite ){
+		sd.guild_invite = 0;
+		sd.guild_invite_account = 0;
+		return false;
+	}
+
+	int i;
+
+	ARR_FIND( 0, g->guild.max_member, i, g->guild.member[i].account_id == 0 );
+
+	if( i == g->guild.max_member ){
+		sd.guild_invite = 0;
+		sd.guild_invite_account = 0;
+
+		if( tsd != nullptr ){
+			clif_guild_inviteack( *tsd, 3 );
 		}
 
-		guild_makemember(&m,sd);
-		intif_guild_addmember(guild_id, &m);
-		//TODO: send a minimap update to this player
+		return true;
 	}
 
-	return 0;
+	struct guild_member m = {};
+
+	guild_makemember( m, sd );
+	intif_guild_addmember( guild_id, m );
+	//TODO: send a minimap update to this player
+
+	return true;
 }
 
 //Invoked when a player joins.
@@ -1072,7 +1118,7 @@ int guild_member_added(int guild_id,uint32 account_id,uint32 char_id,int flag) {
 
 	if (flag == 1) { //failure
 		if( sd2!=nullptr )
-			clif_guild_inviteack(sd2,3);
+			clif_guild_inviteack( *sd2, 3 );
 		return 0;
 	}
 
@@ -1082,12 +1128,12 @@ int guild_member_added(int guild_id,uint32 account_id,uint32 char_id,int flag) {
 	sd->guild = g;
 	//Packets which were sent in the previous 'guild_sent' implementation.
 	clif_guild_belonginfo( *sd );
-	clif_guild_notice(sd);
+	clif_guild_notice( *sd );
 
 	//TODO: send new emblem info to others
 
 	if( sd2!=nullptr )
-		clif_guild_inviteack(sd2,2);
+		clif_guild_inviteack( *sd2, 2 );
 
 	//Next line commented because it do nothing, look at guild_recv_info [LuzZza]
 	//clif_charnameupdate(sd); //Update display name [Skotlex]
@@ -1101,57 +1147,88 @@ int guild_member_added(int guild_id,uint32 account_id,uint32 char_id,int flag) {
 /*==========================================
  * Player request leaving a given guild_id
  *----------------------------------------*/
-int guild_leave(map_session_data* sd, int guild_id, uint32 account_id, uint32 char_id, const char* mes) {
-	nullpo_ret(sd);
+bool guild_leave( map_session_data& sd, int guild_id, uint32 account_id, uint32 char_id, const char* mes ){
+	auto& g = sd.guild;
 
-	if (!sd->guild)
-		return 0;
+	if( g == nullptr ){
+		return false;
+	}
 
-	if(sd->status.account_id!=account_id ||
-		sd->status.char_id!=char_id || sd->status.guild_id!=guild_id ||
-		map_flag_gvg2(sd->bl.m))
-		return 0;
+	if( g->instance_id > 0 && battle_config.instance_block_leave ){
+		return false;
+	}
+
+	if( map_getmapflag( sd.bl.m, MF_GUILDLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 228 ) ); // Guild modification is disabled on this map.
+		return false;
+	}
+
+	if( sd.bg_id ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 670 ) ); // You can't leave battleground guilds.
+		return false;
+	}
+
+	if( sd.status.account_id != account_id || sd.status.char_id != char_id || sd.status.guild_id != guild_id || map_flag_gvg2( sd.bl.m ) ){
+		return false;
+	}
 
 	guild_trade_bound_cancel(sd);
-	intif_guild_leave(sd->status.guild_id, sd->status.account_id, sd->status.char_id,0,mes);
-	return 0;
+
+	return intif_guild_leave( sd.status.guild_id, sd.status.account_id, sd.status.char_id, 0, mes );
 }
 
 /*==========================================
  * Request remove a player to a given guild_id
  *----------------------------------------*/
-int guild_expulsion(map_session_data* sd, int guild_id, uint32 account_id, uint32 char_id, const char* mes) {
-	map_session_data *tsd;
-	int i,ps;
+bool guild_expulsion( map_session_data& sd, int guild_id, uint32 account_id, uint32 char_id, const char* mes ){
+	auto& g = sd.guild;
 
-	nullpo_ret(sd);
+	if( g == nullptr ){
+		return false;
+	}
 
-	auto &g = sd->guild;
+	if( sd.status.guild_id != guild_id ){
+		return false;
+	}
 
-	if (!g)
-		return 0;
+	if( !guild_has_permission( sd, GUILD_PERM_EXPEL ) ){
+		return false;
+	}
 
-	if(sd->status.guild_id!=guild_id)
-		return 0;
+	if( g->instance_id > 0 && battle_config.instance_block_expulsion ){
+		return false;
+	}
+
+	// TODO: for leave this is different messages
+	if( sd.bg_id || map_getmapflag( sd.bl.m, MF_GUILDLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 228 ) ); // Guild modification is disabled on this map.
+		return false;
+	}
 
-	if( (ps=guild_getposition(*sd))<0 || !(g->guild.position[ps].mode&GUILD_PERM_EXPEL) )
-		return 0;	//Expulsion permission
+	map_session_data *tsd = map_id2sd( account_id );
 
 	//Can't leave inside guild castles.
-	if ((tsd = map_id2sd(account_id)) &&
-		tsd->status.char_id == char_id &&
-		map_flag_gvg2(tsd->bl.m))
-		return 0;
+	if( tsd != nullptr && tsd->status.char_id == char_id && map_flag_gvg2( tsd->bl.m ) ){
+		return false;
+	}
 
 	// find the member and perform expulsion
-	i = guild_getindex(g->guild, account_id, char_id);
-	if( i != -1 && strcmp(g->guild.member[i].name,g->guild.master) != 0 ) { //Can't expel the GL!
-		if (tsd)
-			guild_trade_bound_cancel(tsd);
-		intif_guild_leave(g->guild.guild_id,account_id,char_id,1,mes);
+	int i = guild_getindex( g->guild, account_id, char_id );
+
+	if( i < 0 ){
+		return false;
 	}
 
-	return 0;
+	// Can't expel the guild leader
+	if( strcmp( g->guild.member[i].name, g->guild.master ) == 0 ){
+		return false;
+	}
+
+	if( tsd != nullptr ){
+		guild_trade_bound_cancel( *tsd );
+	}
+
+	return intif_guild_leave( g->guild.guild_id, account_id, char_id, 1, mes );
 }
 
 /**
@@ -1187,9 +1264,9 @@ int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int f
 
 
 	if(!flag)
-		clif_guild_leave(online_member_sd, name, mes);
+		clif_guild_leave( *online_member_sd, name, char_id, mes );
 	else
-		clif_guild_expulsion(online_member_sd, name, mes, account_id);
+		clif_guild_expulsion( *online_member_sd, name, char_id, mes );
 
 	// remove member from guild
 	memset(&g->guild.member[i],0,sizeof(struct guild_member));
@@ -1387,7 +1464,7 @@ int guild_send_message(map_session_data *sd, const char *mes, size_t len) {
 /*====================================================
  * Guild receive a message, will be displayed to whole member
  *---------------------------------------------------*/
-int guild_recv_message(int guild_id,uint32 account_id,const char *mes,int len) {
+int guild_recv_message( int guild_id, uint32 account_id, const char *mes, size_t len ){
 	auto g = guild_search(guild_id);
 	if (!g)
 		return 0;
@@ -1473,7 +1550,7 @@ int guild_notice_changed(int guild_id,const char *mes1,const char *mes2) {
 	for(i=0;i<g->guild.max_member;i++){
 		map_session_data *sd = g->guild.member[i].sd;
 		if(sd != nullptr)
-			clif_guild_notice(sd);
+			clif_guild_notice( *sd );
 	}
 	return 0;
 }
@@ -1689,7 +1766,7 @@ int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) {
 	// Inform all members
 	for (i = 0; i < g->guild.max_member; i++)
 		if ((sd = g->guild.member[i].sd) != nullptr)
-			clif_guild_skillinfo(sd);
+			clif_guild_skillinfo( *sd );
 
 	return 0;
 }
@@ -2072,7 +2149,7 @@ int guild_broken(int guild_id,int flag) {
 			sd->status.guild_id=0;
 			sd->guild = nullptr;
 			sd->state.gmaster_flag = 0;
-			clif_guild_broken(g->guild.member[i].sd,0);
+			clif_guild_broken( *sd, 0 );
 			clif_name_area(&sd->bl); // [LuzZza]
 			status_change_end(&sd->bl,SC_LEADERSHIP);
 			status_change_end(&sd->bl,SC_GLORYWOUNDS);
@@ -2101,29 +2178,35 @@ int guild_broken(int guild_id,int flag) {
 * @param guild_id
 * @param sd New guild master
 */
-int guild_gm_change(int guild_id, uint32 char_id) {
-	char *name;
-	int i;
+bool guild_gm_change( int guild_id, uint32 char_id, bool showMessage ){
+	auto g = guild_search( guild_id );
 
-	auto g = guild_search(guild_id);
-	if (!g)
-		return 0;
+	if( g == nullptr ){
+		return false;
+	}
+
+	if( g->instance_id > 0 && battle_config.instance_block_leaderchange ){
+		return false;
+	}
 
-	ARR_FIND(0, MAX_GUILD, i, g->guild.member[i].char_id == char_id);
+	int i;
+
+	ARR_FIND( 0, MAX_GUILD, i, g->guild.member[i].char_id == char_id );
 	
 	if( i == MAX_GUILD ){
 		// Not part of the guild
-		return 0;
+		return false;
 	}
 
-	name = g->guild.member[i].name;
+	char* name = g->guild.member[i].name;
 
-	if (strcmp(g->guild.master, name) == 0) //Nothing to change.
-		return 0;
+	// Nothing to change.
+	if( strcmp( g->guild.master, name ) == 0 ){
+		return false;
+	}
 
 	//Notify servers that master has changed.
-	intif_guild_change_gm(guild_id, name, strlen(name)+1);
-	return 1;
+	return intif_guild_change_gm( guild_id, name, strlen( name ) + 1 );
 }
 
 /** Notification from Char server that a guild's master has changed. [Skotlex]
@@ -2191,7 +2274,7 @@ int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id, time_t tim
 * @param sd Player who breaks the guild
 * @param name Guild name
 */
-int guild_break(map_session_data *sd,char *name) {
+int guild_break( map_session_data& sd, const char* name ){
 	struct unit_data *ud;
 	int i;
 #ifdef BOUND_ITEMS
@@ -2199,20 +2282,23 @@ int guild_break(map_session_data *sd,char *name) {
 	int idxlist[MAX_INVENTORY];
 #endif
 
-	nullpo_ret(sd);
+	auto& g = sd.guild;
 
-	auto &g = sd->guild;
-
-	if (!g)
+	if( g == nullptr ){
 		return 0;
+	}
+
 	if (strcmp(g->guild.name,name) != 0)
 		return 0;
-	if (!sd->state.gmaster_flag)
+
+	if( !sd.state.gmaster_flag ){
 		return 0;
+	}
+
 	for (i = 0; i < g->guild.max_member; i++) {
 		if(	g->guild.member[i].account_id>0 && (
-			g->guild.member[i].account_id!=sd->status.account_id ||
-			g->guild.member[i].char_id!=sd->status.char_id ))
+			g->guild.member[i].account_id != sd.status.account_id ||
+			g->guild.member[i].char_id != sd.status.char_id ) )
 			break;
 	}
 	if (i < g->guild.max_member) {
@@ -2220,11 +2306,22 @@ int guild_break(map_session_data *sd,char *name) {
 		return 0;
 	}
 
-	if (g->instance_id)
+	// Guild locked.
+	if( map_getmapflag( sd.bl.m, MF_GUILDLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 228 ) );
+		return 0;
+	}
+
+	if( g->instance_id ){
+		if( battle_config.instance_block_leave ){
+			return 0;
+		}
+
 		instance_destroy(g->instance_id);
+	}
 
 	/* Regardless of char server allowing it, we clear the guild master's auras */
-	if ((ud = unit_bl2ud(&sd->bl))) {
+	if( ( ud = unit_bl2ud( &sd.bl ) ) ){
 		std::vector<std::shared_ptr<s_skill_unit_group>> group;
 
 		for (const auto su : ud->skillunits) {
@@ -2245,9 +2342,9 @@ int guild_break(map_session_data *sd,char *name) {
 
 #ifdef BOUND_ITEMS
 	//Guild bound item check - Removes the bound flag
-	j = pc_bound_chk(sd,BOUND_GUILD,idxlist);
+	j = pc_bound_chk( &sd, BOUND_GUILD, idxlist );
 	for(i = 0; i < j; i++)
-		pc_delitem(sd,idxlist[i],sd->inventory.u.items_inventory[idxlist[i]].amount,0,1,LOG_TYPE_BOUND_REMOVAL);
+		pc_delitem( &sd,idxlist[i], sd.inventory.u.items_inventory[idxlist[i]].amount, 0, 1, LOG_TYPE_BOUND_REMOVAL );
 #endif
 
 	intif_guild_break(g->guild.guild_id);
@@ -2528,6 +2625,16 @@ bool guild_isallied(int guild_id, int guild_id2) {
 	return( i < MAX_GUILDALLIANCE && g->guild.alliance[i].opposition == 0 );
 }
 
+bool guild_has_permission( map_session_data& sd, enum e_guild_permission permission ){
+	int position = guild_getposition( sd );
+
+	if( position < 0 ){
+		return false;
+	}
+
+	return ( sd.guild->guild.position[position].mode & permission ) != 0;
+}
+
 void guild_flag_add(struct npc_data *nd) {
 	int i;
 

+ 9 - 10
src/map/guild.hpp

@@ -41,6 +41,7 @@ int guild_checkskill(const struct mmo_guild &g,int id);
 bool guild_check_skill_require(const struct mmo_guild &g,uint16 id); // [Komurka]
 int guild_checkcastles(const struct mmo_guild &g); // [MouseJstr]
 bool guild_isallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex]
+bool guild_has_permission( map_session_data& sd, enum e_guild_permission permission );
 
 void do_init_guild(void);
 std::shared_ptr<MapGuild> guild_search(int guild_id);
@@ -53,22 +54,20 @@ int guild_getposition(const map_session_data &sd);
 t_exp guild_payexp(map_session_data *sd,t_exp exp);
 t_exp guild_getexp(map_session_data *sd,t_exp exp); // [Celest]
 
-int guild_create(map_session_data *sd, const char *name);
+bool guild_create( map_session_data& sd, const char* name );
 int guild_created(uint32 account_id,int guild_id);
 int guild_request_info(int guild_id);
 int guild_recv_noinfo(int guild_id);
 int guild_recv_info(const struct mmo_guild &sg);
 int guild_npc_request_info(int guild_id,const char *ev);
-int guild_invite(map_session_data *sd,map_session_data *tsd);
-int guild_reply_invite(map_session_data *sd,int guild_id,int flag);
+bool guild_invite( map_session_data& sd, map_session_data* tsd );
+bool guild_reply_invite( map_session_data& sd, int guild_id, int flag );
 void guild_member_joined(map_session_data *sd);
 int guild_member_added(int guild_id,uint32 account_id,uint32 char_id,int flag);
-int guild_leave(map_session_data *sd,int guild_id,
-	uint32 account_id,uint32 char_id,const char *mes);
+bool guild_leave( map_session_data& sd, int guild_id, uint32 account_id, uint32 char_id, const char *mes );
 int guild_member_withdraw(int guild_id,uint32 account_id,uint32 char_id,int flag,
 	const char *name,const char *mes);
-int guild_expulsion(map_session_data *sd,int guild_id,
-	uint32 account_id,uint32 char_id,const char *mes);
+bool guild_expulsion( map_session_data& sd, int guild_id, uint32 account_id, uint32 char_id, const char *mes );
 void guild_skillup(map_session_data* sd, uint16 skill_id);
 void guild_block_skill(map_session_data *sd, int time);
 int guild_reqalliance(map_session_data *sd,map_session_data *tsd);
@@ -91,12 +90,12 @@ int guild_change_emblem( map_session_data& sd, int len, const char* data );
 int guild_change_emblem_version( map_session_data& sd, int version );
 int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data);
 int guild_send_message(map_session_data *sd, const char *mes, size_t len);
-int guild_recv_message(int guild_id,uint32 account_id,const char *mes,int len);
+int guild_recv_message( int guild_id, uint32 account_id, const char *mes, size_t len );
 int guild_send_dot_remove(map_session_data *sd);
 int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id);
-int guild_break(map_session_data *sd,char *name);
+int guild_break( map_session_data& sd, const char* name );
 int guild_broken(int guild_id,int flag);
-int guild_gm_change(int guild_id, uint32 char_id);
+bool guild_gm_change(int guild_id, uint32 char_id, bool showMessage = false );
 int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id, time_t time);
 
 void guild_castle_map_init(void);

+ 5 - 3
src/map/instance.cpp

@@ -317,7 +317,7 @@ void instance_getsd(int instance_id, map_session_data *&sd, enum send_target *ta
 			(*target) = SELF;
 			break;
 		case IM_CLAN:
-			sd = clan_getavailablesd(clan_search(idata->owner_id));
+			sd = clan_getavailablesd( *clan_search( idata->owner_id ) );
 			(*target) = CLAN;
 	}
 	return;
@@ -351,7 +351,9 @@ static TIMER_FUNC(instance_subscription_timer){
 	std::shared_ptr<MapGuild> gd;
 	struct clan *cd;
 	e_instance_mode mode = idata->mode;
-	int ret = instance_addmap(instance_id); // Check that maps have been added
+
+	// Check that maps have been added
+	size_t ret = instance_addmap( instance_id );
 
 	switch(mode) {
 		case IM_NONE:
@@ -710,7 +712,7 @@ int instance_create(int owner_id, const char *name, e_instance_mode mode) {
  * @param instance_id: Instance ID to add map to
  * @return 0 on failure or map count on success
  */
-int instance_addmap(int instance_id) {
+size_t instance_addmap( int instance_id ){
 	if (instance_id <= 0)
 		return 0;
 

+ 1 - 1
src/map/instance.hpp

@@ -128,7 +128,7 @@ bool instance_addusers(int instance_id);
 bool instance_delusers(int instance_id);
 void instance_generate_mapname(int map_id, int instance_id, char outname[MAP_NAME_LENGTH]);
 int16 instance_mapid(int16 m, int instance_id);
-int instance_addmap(int instance_id);
+size_t instance_addmap( int instance_id );
 
 void instance_addnpc(std::shared_ptr<s_instance_data> idata);
 

+ 23 - 26
src/map/intif.cpp

@@ -204,8 +204,7 @@ int intif_rename(map_session_data *sd, int type, char *name)
  * @param type : Color of msg
  * @return 0=error occured, 1=msg sent
  */
-int intif_broadcast(const char* mes, int len, int type)
-{
+int intif_broadcast( const char* mes, size_t len, int type ){
 	nullpo_ret(mes);
 	if (len < 2)
 		return 0;
@@ -223,7 +222,7 @@ int intif_broadcast(const char* mes, int len, int type)
 
 	WFIFOHEAD(inter_fd, 16 + lp + len);
 	WFIFOW(inter_fd,0)  = 0x3000;
-	WFIFOW(inter_fd,2)  = 16 + lp + len;
+	WFIFOW( inter_fd, 2 )  = static_cast<int16>( 16 + lp + len );
 	WFIFOL(inter_fd,4)  = 0xFF000000; // 0xFF000000 color signals standard broadcast
 	WFIFOW(inter_fd,8)  = 0; // fontType not used with standard broadcast
 	WFIFOW(inter_fd,10) = 0; // fontSize not used with standard broadcast
@@ -249,8 +248,7 @@ int intif_broadcast(const char* mes, int len, int type)
  * @param fontY :
  * @return 0=not send to char-serv, 1=send to char-serv
  */
-int intif_broadcast2(const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY)
-{
+int intif_broadcast2( const char* mes, size_t len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY ){
 	nullpo_ret(mes);
 	if (len < 2)
 		return 0;
@@ -266,7 +264,7 @@ int intif_broadcast2(const char* mes, int len, unsigned long fontColor, short fo
 
 	WFIFOHEAD(inter_fd, 16 + len);
 	WFIFOW(inter_fd,0)  = 0x3000;
-	WFIFOW(inter_fd,2)  = 16 + len;
+	WFIFOW( inter_fd, 2 )  = static_cast<int16>( 16 + len );
 	WFIFOL(inter_fd,4)  = fontColor;
 	WFIFOW(inter_fd,8)  = fontType;
 	WFIFOW(inter_fd,10) = fontSize;
@@ -370,13 +368,15 @@ int intif_wis_reply(int id, int flag)
  */
 int intif_wis_message_to_gm(char *wisp_name, int permission, char *mes)
 {
-	int mes_len;
 	if (CheckForCharServer())
 		return 0;
-	mes_len = strlen(mes) + 1; // + null
+
+	// + null
+	size_t mes_len = strlen( mes ) + 1;
+
 	WFIFOHEAD(inter_fd, mes_len + 8 + NAME_LENGTH);
 	WFIFOW(inter_fd,0) = 0x3003;
-	WFIFOW(inter_fd,2) = mes_len + 32;
+	WFIFOW( inter_fd, 2 ) = static_cast<int16>( mes_len + 32 );
 	safestrncpy(WFIFOCP(inter_fd,4), wisp_name, NAME_LENGTH);
 	WFIFOL(inter_fd,4+NAME_LENGTH) = permission;
 	safestrncpy(WFIFOCP(inter_fd,8+NAME_LENGTH), mes, mes_len);
@@ -444,7 +444,7 @@ int intif_saveregistry(map_session_data *sd)
 		plen += 1;
 
 		safestrncpy(WFIFOCP(inter_fd,plen), varname, len); //the key
-		plen += len;
+		plen += static_cast<decltype(plen)>( len );
 
 		WFIFOL(inter_fd, plen) = script_getvaridx(key.i64);
 		plen += 4;
@@ -468,7 +468,7 @@ int intif_saveregistry(map_session_data *sd)
 				plen += 1;
 
 				safestrncpy(WFIFOCP(inter_fd,plen), p->value, len);
-				plen += len;
+				plen += static_cast<decltype(plen)>( len );
 			} else {
 				script_reg_destroy_single(sd,key.i64,&p->flag);
 			}
@@ -846,15 +846,14 @@ int intif_guild_request_info(int guild_id)
  * @param m : Member to add to the guild
  * @return 0=error, 1=msg_sent
  */
-int intif_guild_addmember(int guild_id,struct guild_member *m)
-{
+int intif_guild_addmember( int guild_id, struct guild_member& m ){
 	if (CheckForCharServer())
 		return 0;
 	WFIFOHEAD(inter_fd,sizeof(struct guild_member)+8);
 	WFIFOW(inter_fd,0) = 0x3032;
 	WFIFOW(inter_fd,2) = sizeof(struct guild_member)+8;
 	WFIFOL(inter_fd,4) = guild_id;
-	memcpy(WFIFOP(inter_fd,8),m,sizeof(struct guild_member));
+	memcpy( WFIFOP( inter_fd, 8 ), &m, sizeof( struct guild_member ) );
 	WFIFOSET(inter_fd,WFIFOW(inter_fd,2));
 	return 1;
 }
@@ -866,17 +865,16 @@ int intif_guild_addmember(int guild_id,struct guild_member *m)
  * @param len : size of the name
  * @return 0=error, 1=msg_sent
  */
-int intif_guild_change_gm(int guild_id, const char* name, int len)
-{
+bool intif_guild_change_gm( int guild_id, const char* name, size_t len ){
 	if (CheckForCharServer())
-		return 0;
+		return false;
 	WFIFOHEAD(inter_fd, len + 8);
 	WFIFOW(inter_fd, 0)=0x3033;
-	WFIFOW(inter_fd, 2)=len+8;
+	WFIFOW( inter_fd, 2 ) = static_cast<int16>( len + 8 );
 	WFIFOL(inter_fd, 4)=guild_id;
 	safestrncpy(WFIFOCP(inter_fd,8),name,len);
 	WFIFOSET(inter_fd,len+8);
-	return 1;
+	return true;
 }
 
 /**
@@ -888,10 +886,9 @@ int intif_guild_change_gm(int guild_id, const char* name, int len)
  * @param mes : quitting message (max 40)
  * @return 0=error, 1=msg_sent
  */
-int intif_guild_leave(int guild_id,uint32 account_id,uint32 char_id,int flag,const char *mes)
-{
+bool intif_guild_leave( int guild_id, uint32 account_id, uint32 char_id, int flag, const char *mes ){
 	if (CheckForCharServer())
-		return 0;
+		return false;
 	WFIFOHEAD(inter_fd, 55);
 	WFIFOW(inter_fd, 0) = 0x3034;
 	WFIFOL(inter_fd, 2) = guild_id;
@@ -900,7 +897,7 @@ int intif_guild_leave(int guild_id,uint32 account_id,uint32 char_id,int flag,con
 	WFIFOB(inter_fd,14) = flag;
 	safestrncpy(WFIFOCP(inter_fd,15),mes,40);
 	WFIFOSET(inter_fd,55);
-	return 1;
+	return true;
 }
 
 /**
@@ -3687,7 +3684,7 @@ void intif_parse_clans( int fd ){
 	clan_load_clandata( ( RFIFOW(fd, 2) - 4 ) / sizeof( struct clan ), (struct clan*)RFIFOP(fd,4) );
 }
 
-int intif_clan_message(int clan_id,uint32 account_id,const char *mes,int len){
+int intif_clan_message( int clan_id, uint32 account_id, const char *mes, size_t len ){
 	if (CheckForCharServer())
 		return 0;
 
@@ -3696,7 +3693,7 @@ int intif_clan_message(int clan_id,uint32 account_id,const char *mes,int len){
 
 	WFIFOHEAD(inter_fd, len + 12);
 	WFIFOW(inter_fd,0)=0x30A1;
-	WFIFOW(inter_fd,2)=len+12;
+	WFIFOW( inter_fd, 2 ) = static_cast<uint16>( len + 12 );
 	WFIFOL(inter_fd,4)=clan_id;
 	WFIFOL(inter_fd,8)=account_id;
 	safestrncpy(WFIFOCP(inter_fd,12),mes,len);
@@ -3750,7 +3747,7 @@ int intif_parse_clan_onlinecount( int fd ){
 
 	clan->connect_member = RFIFOW(fd,6);
 
-	clif_clan_onlinecount(clan);
+	clif_clan_onlinecount( *clan );
 
 	return 1;
 }

+ 6 - 6
src/map/intif.hpp

@@ -23,8 +23,8 @@ class map_session_data;
 
 int intif_parse(int fd);
 
-int intif_broadcast(const char* mes, int len, int type);
-int intif_broadcast2(const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY);
+int intif_broadcast( const char* mes, size_t len, int type );
+int intif_broadcast2( const char* mes, size_t len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY );
 int intif_broadcast_obtain_special_item(map_session_data *sd, t_itemid nameid, unsigned int sourceid, unsigned char type);
 int intif_broadcast_obtain_special_item_npc(map_session_data *sd, t_itemid nameid);
 int intif_main_message(map_session_data* sd, const char* message);
@@ -52,12 +52,12 @@ int intif_party_sharelvlupdate(unsigned int share_lvl);
 
 int intif_guild_create(const char *name, const struct guild_member *master);
 int intif_guild_request_info(int guild_id);
-int intif_guild_addmember(int guild_id, struct guild_member *m);
-int intif_guild_leave(int guild_id, uint32 account_id, uint32 char_id, int flag, const char *mes);
+int intif_guild_addmember( int guild_id, struct guild_member& m );
+bool intif_guild_leave(int guild_id, uint32 account_id, uint32 char_id, int flag, const char *mes);
 int intif_guild_memberinfoshort(int guild_id, uint32 account_id, uint32 char_id, int online, int lv, int class_);
 int intif_guild_break(int guild_id);
 int intif_guild_message(int guild_id, uint32 account_id, const char *mes, size_t len);
-int intif_guild_change_gm(int guild_id, const char* name, int len);
+bool intif_guild_change_gm( int guild_id, const char* name, size_t len );
 int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len);
 int intif_guild_change_memberinfo(int guild_id, uint32 account_id, uint32 char_id, int type, const void *data, int len);
 int intif_guild_position(int guild_id, int idx, struct guild_position *p);
@@ -116,7 +116,7 @@ int intif_elemental_delete(int ele_id);
 int intif_elemental_save(struct s_elemental *ele);
 // CLAN SYSTEM
 int intif_clan_requestclans();
-int intif_clan_message(int clan_id,uint32 account_id,const char *mes,int len);
+int intif_clan_message( int clan_id, uint32 account_id, const char *mes, size_t len );
 int intif_clan_member_joined( int clan_id );
 int intif_clan_member_left( int clan_id );
 // ACHIEVEMENT SYSTEM

+ 60 - 24
src/map/itemdb.cpp

@@ -1158,6 +1158,29 @@ void ItemDatabase::loadingFinished(){
 			}
 		}
 
+		if (item->type != IT_ARMOR && item->type != IT_SHADOWGEAR && item->def > 0) {
+			ShowWarning( "Item %s is not a armor or shadowgear. Defaulting Defense to 0.\n", item->name.c_str() );
+			item->def = 0;
+		}
+
+		if (item->type != IT_WEAPON && item->type != IT_AMMO && item->atk > 0) {
+			ShowWarning( "Item %s is not a weapon or ammo. Defaulting Attack to 0.\n", item->name.c_str() );
+			item->atk = 0;
+		}
+
+		if (item->type != IT_WEAPON) {
+#ifdef RENEWAL
+			if (item->matk > 0) {
+				ShowWarning( "Item %s is not a weapon. Defaulting MagicAttack to 0.\n", item->name.c_str() );
+				item->matk = 0;
+			}
+#endif
+			if (item->range > 0) {
+				ShowWarning( "Item %s is not a weapon. Defaulting Range to 0.\n", item->name.c_str() );
+				item->range = 0;
+			}
+		}
+
 		// When a particular price is not given, we should base it off the other one
 		if (!hasPriceValue[item->nameid].has_buy && hasPriceValue[item->nameid].has_sell)
 			item->value_buy = item->value_sell * 2;
@@ -2921,7 +2944,7 @@ t_itemid ItemGroupDatabase::get_random_item_id(uint16 group_id, uint8 sub_group)
 * @param group_id: The group ID of item that obtained by player
 * @param *group: struct s_item_group from itemgroup_db[group_id].random[idx] or itemgroup_db[group_id].must[sub_group][idx]
 */
-static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std::shared_ptr<s_item_group_entry> data) {
+void ItemGroupDatabase::pc_get_itemgroup_sub( map_session_data& sd, bool identify, std::shared_ptr<s_item_group_entry> data ){
 	if (data == nullptr)
 		return;
 
@@ -2934,8 +2957,8 @@ static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std
 	if (data->isNamed) {
 		tmp.card[0] = itemdb_isequip(data->nameid) ? CARD0_FORGE : CARD0_CREATE;
 		tmp.card[1] = 0;
-		tmp.card[2] = GetWord(sd->status.char_id, 0);
-		tmp.card[3] = GetWord(sd->status.char_id, 1);
+		tmp.card[2] = GetWord( sd.status.char_id, 0 );
+		tmp.card[3] = GetWord( sd.status.char_id, 1 );
 	}
 
 	uint16 get_amt = 0;
@@ -2949,8 +2972,7 @@ static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std
 
 	// Do loop for non-stackable item
 	for (uint16 i = 0; i < data->amount; i += get_amt) {
-		char flag = 0;
-		tmp.unique_id = data->GUID ? pc_generate_unique_id(sd) : 0; // Generate GUID
+		tmp.unique_id = data->GUID ? pc_generate_unique_id( &sd ) : 0; // Generate GUID
 
 		if( itemdb_isequip( data->nameid ) ){
 			if( data->refineMinimum > 0 && data->refineMaximum > 0 ){
@@ -2970,13 +2992,15 @@ static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std
 			}
 		}
 
-		if ((flag = pc_additem(sd, &tmp, get_amt, LOG_TYPE_SCRIPT))) {
-			clif_additem(sd, 0, 0, flag);
-			if (pc_candrop(sd, &tmp))
-				map_addflooritem(&tmp, tmp.amount, sd->bl.m, sd->bl.x,sd->bl.y, 0, 0, 0, 0, 0);
+		e_additem_result flag = pc_additem( &sd, &tmp, get_amt, LOG_TYPE_SCRIPT );
+
+		if( flag == ADDITEM_SUCCESS ){
+			if( data->isAnnounced ){
+				intif_broadcast_obtain_special_item( &sd, data->nameid, sd.itemid, ITEMOBTAIN_TYPE_BOXITEM );
+			}
+		}else{
+			clif_additem( &sd, 0, 0, flag );
 		}
-		else if (!flag && data->isAnnounced)
-			intif_broadcast_obtain_special_item(sd, data->nameid, sd->itemid, ITEMOBTAIN_TYPE_BOXITEM);
 	}
 }
 
@@ -2984,11 +3008,9 @@ static void itemdb_pc_get_itemgroup_sub(map_session_data *sd, bool identify, std
 * Find item(s) that will be obtained by player based on Item Group
 * @param group_id: The group ID that will be gained by player
 * @param nameid: The item that trigger this item group
-* @return val: 0:success, 1:no sd, 2:invalid item group
+* @return val: 0:success, 2:invalid item group
 */
-uint8 ItemGroupDatabase::pc_get_itemgroup(uint16 group_id, bool identify, map_session_data *sd) {
-	nullpo_retr(1,sd);
-
+uint8 ItemGroupDatabase::pc_get_itemgroup( uint16 group_id, bool identify, map_session_data& sd ){
 	std::shared_ptr<s_item_group_db> group = this->find(group_id);
 
 	if (group == nullptr) {
@@ -2997,21 +3019,22 @@ uint8 ItemGroupDatabase::pc_get_itemgroup(uint16 group_id, bool identify, map_se
 	}
 	if (group->random.empty())
 		return 0;
-	
-	// Get all the 'must' item(s) (subgroup 0)
-	uint16 subgroup = 0;
-	std::shared_ptr<s_item_group_random> random = util::umap_find(group->random, subgroup);
 
-	if (random != nullptr && !random->data.empty()) {
-		for (const auto &it : random->data)
-			itemdb_pc_get_itemgroup_sub(sd, identify, it.second);
+	// Get all the 'must' item(s) (subgroup 0)
+	std::shared_ptr<s_item_group_random> must = util::umap_find(group->random, static_cast<uint16>(0));
+	if( must != nullptr ){
+		for (const auto &it : must->data)
+			this->pc_get_itemgroup_sub( sd, identify, it.second );
 	}
 
 	// Get 1 'random' item from each subgroup
 	for (const auto &random : group->random) {
-		if (random.first == 0 || random.second->data.empty())
+		// Skip the 'must' group
+		if( random.first == 0 ){
 			continue;
-		itemdb_pc_get_itemgroup_sub(sd, identify, get_random_itemsubgroup(random.second));
+		}
+
+		this->pc_get_itemgroup_sub( sd, identify, get_random_itemsubgroup( random.second ) );
 	}
 
 	return 0;
@@ -3538,6 +3561,19 @@ uint64 ItemGroupDatabase::parseBodyNode(const ryml::NodeRef& node) {
 }
 
 void ItemGroupDatabase::loadingFinished() {
+	// Delete empty sub groups
+	for( const auto &group : *this ){
+		for( auto it = group.second->random.begin(); it != group.second->random.end(); ){
+			if( it->second->data.empty() ){
+				ShowDebug( "Deleting empty subgroup %u from item group %hu.\n", it->first, group.first );
+				it = group.second->random.erase( it );
+			}else{
+				it++;
+			}
+		}
+	}
+
+	// Calculate rates
 	for (const auto &group : *this) {
 		for (const auto &random : group.second->random) {
 			random.second->total_rate = 0;

+ 5 - 1
src/map/itemdb.hpp

@@ -2824,6 +2824,7 @@ enum e_random_item_group {
 	IG_AEGIS_103034,
 	IG_P_BOOSTER_CALL_PACKAGE,
 	IG_P_COMPENSATION_BOX,
+	IG_ENCHANT_STONE_BOX35,
 
 	IG_MAX,
 };
@@ -3172,7 +3173,10 @@ public:
 	int16 item_exists_pc(map_session_data *sd, uint16 group_id);
 	t_itemid get_random_item_id(uint16 group_id, uint8 sub_group);
 	std::shared_ptr<s_item_group_entry> get_random_entry(uint16 group_id, uint8 sub_group);
-	uint8 pc_get_itemgroup(uint16 group_id, bool identify, map_session_data *sd);
+	uint8 pc_get_itemgroup( uint16 group_id, bool identify, map_session_data& sd );
+
+private:
+	void pc_get_itemgroup_sub( map_session_data& sd, bool identify, std::shared_ptr<s_item_group_entry> data );
 };
 
 extern ItemGroupDatabase itemdb_group;

+ 3 - 0
src/map/map-server-generator.vcxproj

@@ -130,6 +130,7 @@
       <PreprocessorDefinitions>$(DefineConstants);MAP_GENERATOR;WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\json\include;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -151,6 +152,7 @@
       <PreprocessorDefinitions>$(DefineConstants);MAP_GENERATOR;WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\json\include;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -175,6 +177,7 @@
       <PreprocessorDefinitions>$(DefineConstants);MAP_GENERATOR;WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\json\include;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 3 - 0
src/map/map-server.vcxproj

@@ -130,6 +130,7 @@
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -151,6 +152,7 @@
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -175,6 +177,7 @@
       <PreprocessorDefinitions>$(DefineConstants);WIN32;FD_SETSIZE=4096;PCRE_SUPPORT;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <DisableSpecificWarnings>4018</DisableSpecificWarnings>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)\3rdparty\rapidyaml\src;$(SolutionDir)\3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)\3rdparty\pcre\include;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

+ 84 - 41
src/map/map.cpp

@@ -2181,44 +2181,72 @@ void map_clearflooritem(struct block_list *bl) {
 	map_freeblock(&fitem->bl);
 }
 
-/*==========================================
- * (m,x,y) locates a random available free cell around the given coordinates
- * to place an BL_ITEM object. Scan area is 9x9, returns 1 on success.
- * x and y are modified with the target cell when successful.
- *------------------------------------------*/
-int map_searchrandfreecell(int16 m,int16 *x,int16 *y,int stack) {
-	int free_cell,i,j;
-	int free_cells[9][2];
-	struct map_data *mapdata = map_getmapdata(m);
-
-	if( mapdata == nullptr || mapdata->block == nullptr ){
-		return 0;
+/**
+ * Returns if a cell is passable and not occupied by given types of block
+ * Cells 5 cells from the SW edge and 4 cells from the NE edge are never considered as free
+ * @param m: Map of cell to check
+ * @param x: X-coordinate of cell to check
+ * @param y: Y-coordinate of cell to check
+ * @param type: Types of block to check for
+ * @return True if cell is passable, not on the edge and not occupied by given types of block
+ */
+bool map_cell_free(int16 m, int16 x, int16 y, int type)
+{
+	struct map_data* mapdata = map_getmapdata(m);
+	if (mapdata == nullptr || mapdata->block == nullptr) {
+		return false;
 	}
 
-	for(free_cell=0,i=-1;i<=1;i++){
-		if(i+*y<0 || i+*y>=mapdata->ys)
-			continue;
-		for(j=-1;j<=1;j++){
-			if(j+*x<0 || j+*x>=mapdata->xs)
-				continue;
-			if(map_getcell(m,j+*x,i+*y,CELL_CHKNOPASS) && !map_getcell(m,j+*x,i+*y,CELL_CHKICEWALL))
-				continue;
-			//Avoid item stacking to prevent against exploits. [Skotlex]
-			if(stack && map_count_oncell(m,j+*x,i+*y, BL_ITEM, 0) > stack)
-				continue;
-			free_cells[free_cell][0] = j+*x;
-			free_cells[free_cell++][1] = i+*y;
+	// Cells outside the map or within 4-5 cells of the map edge are considered invalid officially
+	// Note that this isn't symmetric (NE - 4 cells, SW - 5 cells)
+	// If for some reason edge size was set to below 5 cells, we consider them as valid
+	int16 edge_valid = std::min(battle_config.map_edge_size, 5);
+	if (x < edge_valid || x > mapdata->xs - edge_valid || y < edge_valid || y > mapdata->ys - edge_valid)
+		return false;
+	if (map_getcell(m, x, y, CELL_CHKNOPASS))
+		return false;
+	if (map_count_oncell(m, x, y, type, 0) > 0)
+		return false;
+
+	return true;
+}
+
+/**
+ * Locates a random available free cell around the given coordinates within a given distance range.
+ * This uses the official algorithm that checks each quadrant and line once.
+ * x and y are modified with the target free cell when successful.
+ * @param m: Map to search
+ * @param x: X-coordinate around which free cell is searched
+ * @param y: Y-coordinate around which free cell is searched
+ * @param distmin: Minimum distance from the given cell
+ * @param distmax: Maximum distance from the given cell
+ * @param type: If the given types of block are present on the cell, it counts as occupied
+ * @return True if free cell could be found
+ */
+bool map_search_freecell_dist(int16 m, int16* x, int16* y, int16 distmin, int16 distmax, int type)
+{
+	// This is to prevent that always the same quadrant is checked first 
+	int16 mirrorx = (rnd()%2) ? -1 : 1;
+	int16 mirrory = (rnd()%2) ? -1 : 1;
+
+	for (int16 i = -1; i <= 1; i++) {
+		for (int16 j = -1; j <= 1; j++) {
+			if (i || j)
+			{
+				int16 checkX = *x + mirrorx * i * rnd_value(distmin, distmax);
+				int16 checkY = *y + mirrory * j * rnd_value(distmin, distmax);
+				if (map_cell_free(m, checkX, checkY, type))
+				{
+					*x = checkX;
+					*y = checkY;
+					return true;
+				}
+			}
 		}
 	}
-	if(free_cell==0)
-		return 0;
-	free_cell = rnd_value(0, free_cell-1);
-	*x = free_cells[free_cell][0];
-	*y = free_cells[free_cell][1];
-	return 1;
+	return false;
 }
 
-
 static int map_count_sub(struct block_list *bl,va_list ap)
 {
 	return 1;
@@ -2397,12 +2425,14 @@ bool map_closest_freecell(int16 m, int16 *x, int16 *y, int type, int flag)
  * @param first_charid : 1st player that could loot the item (only charid that could loot for first_get_tick duration)
  * @param second_charid :  2nd player that could loot the item (2nd charid that could loot for second_get_charid duration)
  * @param third_charid : 3rd player that could loot the item (3rd charid that could loot for third_get_charid duration)
- * @param flag: &1 MVP item. &2 do stacking check. &4 bypass droppable check.
+ * @param flag: &1 MVP item. &2 search free cell in 5x5 area instead of 3x3. &4 bypass droppable check.
  * @param mob_id: Monster ID if dropped by monster
  * @param canShowEffect: enable pillar effect on the dropped item (if set in the database)
+ * @param dir: where the item should drop around the target (DIR_MAX: random cell around center)
+ * @param type: types of block the item should not stack on
  * @return 0:failure, x:item_gid [MIN_FLOORITEM;MAX_FLOORITEM]==[2;START_ACCOUNT_NUM]
  *------------------------------------------*/
-int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect)
+int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect, enum directions dir, int type)
 {
 	struct flooritem_data *fitem = nullptr;
 
@@ -2411,8 +2441,18 @@ int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, i
 	if (!(flags&4) && battle_config.item_onfloor && (itemdb_traderight(item->nameid).trade))
 		return 0; //can't be dropped
 
-	if (!map_searchrandfreecell(m,&x,&y,flags&2?1:0))
-		return 0;
+	if (dir > DIR_CENTER && dir < DIR_MAX) {
+		x += dirx[dir];
+		y += diry[dir];
+	}
+	// If cell occupied and not center cell, drop item around the drop target cell
+	if (dir == DIR_MAX || (dir != DIR_CENTER && !map_cell_free(m, x, y, type))) {
+		if (!map_search_freecell_dist(m, &x, &y, 1, (flags&2)?2:1, type)) {
+			// Only stop here if BL_ITEM shall not stack, otherwise drop on original target cell
+			if (type&BL_ITEM)
+				return 0;
+		}
+	}
 
 	CREATE(fitem, struct flooritem_data, 1);
 	fitem->bl.type=BL_ITEM;
@@ -2631,11 +2671,11 @@ int map_quit(map_session_data *sd) {
 		bg_queue_leave(sd, false);
 
 	if( sd->status.clan_id )
-		clan_member_left(sd);
+		clan_member_left( *sd );
 
 	pc_itemcd_do(sd,false);
 
-	npc_script_event(sd, NPCE_LOGOUT);
+	npc_script_event( *sd, NPCE_LOGOUT );
 
 	//Unit_free handles clearing the player related data,
 	//map_quit handles extra specific data which is related to quitting normally
@@ -2673,7 +2713,9 @@ int map_quit(map_session_data *sd) {
 	}
 
 	// Return loot to owner
-	if( sd->pd ) pet_lootitem_drop(sd->pd, sd);
+	if( sd->pd != nullptr ){
+		pet_lootitem_drop( *sd->pd, sd );
+	}
 
 	if (sd->ed) // Remove effects here rather than unit_remove_map_pc so we don't clear on Teleport/map change.
 		elemental_clean_effect(sd->ed);
@@ -4230,7 +4272,7 @@ int map_readgat (struct map_data* m)
 
 	sprintf(filename, "data\\%s.gat", m->name);
 
-	gat = (uint8 *) grfio_read(filename);
+	gat = (uint8 *) grfio_reads(filename);
 	if (gat == nullptr)
 		return 0;
 
@@ -5018,7 +5060,8 @@ enum e_mapflag map_getmapflag_by_name(char* name)
 bool map_getmapflag_name( enum e_mapflag mapflag, char* output ){
 	const char* constant;
 	const char* prefix = "mf_";
-	int i, len = strlen(prefix);
+	size_t i;
+	size_t len = strlen( prefix );
 
 	// Look it up
 	constant = script_get_constant_str( prefix, mapflag );

+ 4 - 1
src/map/map.hpp

@@ -24,6 +24,7 @@
 
 #include "navi.hpp"
 #include "script.hpp"
+#include "path.hpp"
 
 using namespace rathena;
 using rathena::server_core::Core;
@@ -372,6 +373,8 @@ enum e_race2 : uint8{
 	RC2_ILLUSION_TURTLE,
 	RC2_RACHEL_SANCTUARY,
 	RC2_ILLUSION_LUANDA,
+	RC2_ILLUSION_FROZEN,
+	RC2_ILLUSION_MOONLIGHT,
 	RC2_MAX
 };
 
@@ -1192,7 +1195,7 @@ bool map_addnpc(int16 m,struct npc_data *);
 TIMER_FUNC(map_clearflooritem_timer);
 TIMER_FUNC(map_removemobs_timer);
 void map_clearflooritem(struct block_list* bl);
-int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false);
+int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false, enum directions dir = DIR_MAX, int type = BL_NUL);
 
 // instances
 int map_addinstancemap(int src_m, int instance_id, bool no_mapflag);

+ 180 - 145
src/map/mob.cpp

@@ -80,9 +80,8 @@ struct s_mob_skill_db {
 
 std::unordered_map<int32, std::shared_ptr<s_mob_skill_db>> mob_skill_db; /// Monster skill temporary db. s_mob_skill_db -> mobid
 
-static struct eri *item_drop_ers; //For loot drops delay structures.
-static struct eri *item_drop_list_ers;
-
+std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> mob_delayed_drops;
+std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> mob_looted_drops;
 MobSummonDatabase mob_summon_db;
 MobChatDatabase mob_chat_db;
 MapDropDatabase map_drop_db;
@@ -192,6 +191,7 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time)
 		mvptomb_destroy(md);
 
 	CREATE(nd, struct npc_data, 1);
+	new (nd) npc_data();
 
 	nd->bl.id = md->tomb_nid = npc_get_new_npc_id();
 
@@ -204,13 +204,17 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time)
 	safestrncpy(nd->name, msg_txt(nullptr,656), sizeof(nd->name));
 
 	nd->class_ = 565;
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 	nd->subtype = NPCTYPE_TOMB;
 
 	nd->u.tomb.md = md;
 	nd->u.tomb.kill_time = time;
 	nd->u.tomb.spawn_timer = INVALID_TIMER;
 
+	nd->dynamicnpc.owner_char_id = 0;
+	nd->dynamicnpc.last_interaction = 0;
+	nd->dynamicnpc.removal_tid = INVALID_TIMER;
+
 	if (killer)
 		safestrncpy(nd->u.tomb.killer_name, killer, NAME_LENGTH);
 	else
@@ -373,8 +377,10 @@ e_mob_bosstype s_mob_db::get_bosstype(){
 }
 
 e_mob_bosstype mob_data::get_bosstype(){
-	if( this->db != nullptr ){
-		return this->db->get_bosstype();
+	if( status_has_mode( &this->status, MD_MVP ) ){
+		return BOSSTYPE_MVP;
+	}else if( this->status.class_ == CLASS_BOSS ){
+		return BOSSTYPE_MINIBOSS;
 	}else{
 		return BOSSTYPE_NONE;
 	}
@@ -2176,73 +2182,95 @@ static TIMER_FUNC(mob_ai_hard){
  * @param mobdrop: Drop data
  * @author [Cydh]
  **/
-void mob_setdropitem_option(item *item, s_mob_drop *mobdrop) {
-	if (!item || !mobdrop)
-		return;
-
-	std::shared_ptr<s_random_opt_group> group = random_option_group.find(mobdrop->randomopt_group);
+void mob_setdropitem_option( item& item, s_mob_drop& mobdrop ){
+	std::shared_ptr<s_random_opt_group> group = random_option_group.find( mobdrop.randomopt_group );
 
 	if (group != nullptr) {
-		group->apply( *item );
+		group->apply( item );
 	}
 }
 
 /*==========================================
  * Initializes the delay drop structure for mob-dropped items.
  *------------------------------------------*/
-static struct item_drop* mob_setdropitem(struct s_mob_drop *mobdrop, int qty, unsigned short mob_id)
-{
-	struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop);
-	memset(&drop->item_data, 0, sizeof(struct item));
-	drop->item_data.nameid = mobdrop->nameid;
+static std::shared_ptr<s_item_drop> mob_setdropitem( s_mob_drop& mobdrop, int qty, unsigned short mob_id ){
+	std::shared_ptr<s_item_drop> drop = std::make_shared<s_item_drop>();
+
+	drop->item_data = { 0 };
+	drop->item_data.nameid = mobdrop.nameid;
 	drop->item_data.amount = qty;
-	drop->item_data.identify = itemdb_isidentified(mobdrop->nameid);
-	mob_setdropitem_option(&drop->item_data, mobdrop);
+	drop->item_data.identify = itemdb_isidentified( mobdrop.nameid );
+	mob_setdropitem_option( drop->item_data, mobdrop );
 	drop->mob_id = mob_id;
-	drop->next = nullptr;
+
 	return drop;
 }
 
 /*==========================================
  * Initializes the delay drop structure for mob-looted items.
  *------------------------------------------*/
-static struct item_drop* mob_setlootitem(struct s_mob_lootitem *item, unsigned short mob_id)
-{
-	struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop);
-	memcpy(&drop->item_data, item, sizeof(struct item));
+static std::shared_ptr<s_item_drop> mob_setlootitem( s_mob_lootitem& item, unsigned short mob_id ){
+	std::shared_ptr<s_item_drop> drop = std::make_shared<s_item_drop>();
+
+	memcpy( &drop->item_data, &item, sizeof( struct item ) );
 
 	/**
-	 * Conditions for lotted item, so it can be announced when player pick it up
+	 * Conditions for looted item, so it can be announced when player pick it up
 	 * 1. Not-dropped other than monster. (This will be done later on pc_takeitem/party_share_loot)
-	 * 2. The mob_id is become the last lootter, instead of the real monster who drop it.
+	 * 2. The mob_id becomes the last looter, instead of the real monster who drop it.
 	 **/
-	drop->mob_id = item->mob_id;
+	drop->mob_id = item.mob_id;
 
-	drop->next = nullptr;
 	return drop;
 }
 
+/**
+ * Makes all items from a drop list drop
+ * @param list: list with all items that should drop
+ * @param loot: whether the items in the list are new drops or previously looted items
+ */
+void mob_process_drop_list(std::shared_ptr<s_item_drop_list>& list, bool loot)
+{
+	// First regular drop always drops at center
+	enum directions dir = DIR_CENTER;
+	// Looted drops start north instead
+	if (loot)
+		dir = DIR_NORTH;
+
+	for (std::shared_ptr<s_item_drop>& ditem : list->items) {
+		map_addflooritem(&ditem->item_data, ditem->item_data.amount,
+			list->m, list->x, list->y,
+			list->first_charid, list->second_charid, list->third_charid, 4, ditem->mob_id, !loot, dir, BL_CHAR|BL_PET);
+		// The drop location loops between three locations: SE -> W -> N -> SE
+		if (dir <= DIR_NORTH)
+			dir = DIR_SOUTHEAST;
+		else if (dir == DIR_SOUTHEAST)
+			dir = DIR_WEST;
+		else
+			dir = DIR_NORTH;
+	}
+}
+
 /*==========================================
  * item drop with delay (timer function)
  *------------------------------------------*/
-static TIMER_FUNC(mob_delay_item_drop){
-	struct item_drop_list *list;
-	struct item_drop *ditem;
+static TIMER_FUNC(mob_delay_item_drop) {
+	uint32 bl_id = static_cast<uint32>(id);
 
-	list = (struct item_drop_list *)data;
-	ditem = list->item;
+	// Regular drops
+	std::shared_ptr<s_item_drop_list> list = util::umap_find(mob_delayed_drops, bl_id);
+	if (list != nullptr) {
+		mob_process_drop_list(list, false);
+		mob_delayed_drops.erase(bl_id);
+	}
 
-	while (ditem) {
-		struct item_drop *ditem_prev;
-		map_addflooritem(&ditem->item_data,ditem->item_data.amount,
-			list->m,list->x,list->y,
-			list->first_charid,list->second_charid,list->third_charid,4,ditem->mob_id,true);
-		ditem_prev = ditem;
-		ditem = ditem->next;
-		ers_free(item_drop_ers, ditem_prev);
+	// Looted drops
+	list = util::umap_find(mob_looted_drops, bl_id);
+	if (list != nullptr) {
+		mob_process_drop_list(list, true);
+		mob_looted_drops.erase(bl_id);
 	}
 
-	ers_free(item_drop_list_ers, list);
 	return 0;
 }
 
@@ -2252,7 +2280,7 @@ static TIMER_FUNC(mob_delay_item_drop){
  * rate is the drop-rate of the item, required for autoloot.
  * flag : Killed only by homunculus/mercenary?
  *------------------------------------------*/
-static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate, bool flag)
+static void mob_item_drop(struct mob_data *md, std::shared_ptr<s_item_drop_list>& dlist, std::shared_ptr<s_item_drop>& ditem, int loot, int drop_rate, bool flag)
 {
 	TBL_PC* sd;
 	bool test_autoloot;
@@ -2281,12 +2309,11 @@ static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, str
 		if (party_share_loot(party_search(sd->status.party_id),
 			sd, &ditem->item_data, sd->status.char_id) == 0
 		) {
-			ers_free(item_drop_ers, ditem);
 			return;
 		}
 	}
-	ditem->next = dlist->item;
-	dlist->item = ditem;
+
+	dlist->items.push_back( ditem );
 }
 
 TIMER_FUNC(mob_timer_delete){
@@ -2847,117 +2874,136 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 
 	} //End EXP giving.
 
+	// Looted items have an independent drop position and also don't show special effects when dropped
+	// So we need put them into a separate list
+	std::shared_ptr<s_item_drop_list> lootlist = std::make_shared<s_item_drop_list>();
+	lootlist->m = md->bl.m;
+	lootlist->x = md->bl.x;
+	lootlist->y = md->bl.y;
+	lootlist->first_charid = (mvp_sd ? mvp_sd->status.char_id : 0);
+	lootlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
+	lootlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
+
+	// Process items looted by the mob
+	if (md->lootitems) {
+		for (i = 0; i < md->lootitem_count; i++) {
+			std::shared_ptr<s_item_drop> ditem = mob_setlootitem(md->lootitems[i], md->mob_id);
+			mob_item_drop(md, lootlist, ditem, 1, 10000, homkillonly || merckillonly);
+		}
+	}
+
 	if( !(type&1) && !mapdata->getMapFlag(MF_NOMOBLOOT) && !md->state.rebirth && (
 		!md->special_state.ai || //Non special mob
 		battle_config.alchemist_summon_reward == 2 || //All summoned give drops
 		(md->special_state.ai==AI_SPHERE && battle_config.alchemist_summon_reward == 1) //Marine Sphere Drops items.
 		) )
 	{ // Item Drop
-		struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list);
-		struct item_drop *ditem;
 		int drop_rate, drop_modifier = 100;
 
 #ifdef RENEWAL_DROP
 		drop_modifier = pc_level_penalty_mod( mvp_sd != nullptr ? mvp_sd : second_sd != nullptr ? second_sd : third_sd, PENALTY_DROP, nullptr, md );
 #endif
+
+		std::shared_ptr<s_item_drop_list> dlist = std::make_shared<s_item_drop_list>();
 		dlist->m = md->bl.m;
 		dlist->x = md->bl.x;
 		dlist->y = md->bl.y;
 		dlist->first_charid = (mvp_sd ? mvp_sd->status.char_id : 0);
 		dlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
 		dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
-		dlist->item = nullptr;
 
+		// Ore Discovery [Celest]
+		if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rnd()%10000) {
+			s_mob_drop mobdrop = {};
+
+			mobdrop.nameid = itemdb_group.get_random_item_id(IG_FINDINGORE,1);
+
+			std::shared_ptr<s_item_drop> ditem = mob_setdropitem( mobdrop, 1, md->mob_id );
+
+			mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, homkillonly || merckillonly);
+		}
+
+		if(sd) {
+			// process script-granted extra drop bonuses
+			t_itemid dropid = 0;
+
+			if (mapdata->getMapFlag(MF_NOBONUSITEMDROP)) {
+        for (const auto &it : sd->add_drop) {
+          if (!&it || (!it.nameid && !it.group))
+            continue;
+          if ((it.race < RC_NONE_ && it.race == -md->mob_id) || //Race < RC_NONE_, use mob_id
+            (it.race == RC_ALL || it.race == status->race) || //Matched race
+            (it.class_ == CLASS_ALL || it.class_ == status->class_)) //Matched class
+          {
+            //Check if the bonus item drop rate should be multiplied with mob level/10 [Lupus]
+            if (it.rate < 0) {
+              //It's negative, then it should be multiplied. with mob_level/10
+              //rate = base_rate * (mob_level/10) + 1
+              drop_rate = (-it.rate) * md->level / 10 + 1;
+              drop_rate = cap_value(drop_rate, max(battle_config.item_drop_adddrop_min,1), min(battle_config.item_drop_adddrop_max,10000));
+            }
+            else
+              //it's positive, then it goes as it is
+              drop_rate = it.rate;
+
+            if (rnd()%10000 >= drop_rate)
+              continue;
+            dropid = (it.nameid > 0) ? it.nameid : itemdb_group.get_random_item_id(it.group,1);
+
+            s_mob_drop mobdrop = {};
+
+            mobdrop.nameid = dropid;
+
+            std::shared_ptr<s_item_drop> ditem = mob_setdropitem(mobdrop, 1, md->mob_id);
+
+            mob_item_drop( md, dlist, ditem, 0, drop_rate, homkillonly || merckillonly );
+          }
+        }
+			}
+
+			// process script-granted zeny bonus (get_zeny_num) [Skotlex]
+			if( sd->bonus.get_zeny_num && rnd()%100 < sd->bonus.get_zeny_rate ) {
+				i = sd->bonus.get_zeny_num > 0 ? sd->bonus.get_zeny_num : -md->level * sd->bonus.get_zeny_num;
+				if (!i) i = 1;
+				pc_getzeny(sd, 1+rnd()%i, LOG_TYPE_PICKDROP_MONSTER);
+			}
+		}
+
+		// Regular mob drops drop after script-granted drops
 		for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
 			if (md->db->dropitem[i].nameid == 0)
 				continue;
 
 			std::shared_ptr<item_data> it = item_db.find(md->db->dropitem[i].nameid);
 
-			if ( it == nullptr )
+			if (it == nullptr)
 				continue;
-			
+
 			drop_rate = mob_getdroprate(src, md->db, md->db->dropitem[i].rate, drop_modifier, md);
 
 			// attempt to drop the item
 			if (rnd() % 10000 >= drop_rate)
 				continue;
 
-			if( mvp_sd && it->type == IT_PETEGG ) {
+			if (mvp_sd && it->type == IT_PETEGG) {
 				pet_create_egg(mvp_sd, md->db->dropitem[i].nameid);
 				continue;
 			}
 
-			ditem = mob_setdropitem(&md->db->dropitem[i], 1, md->mob_id);
+			std::shared_ptr<s_item_drop> ditem = mob_setdropitem(md->db->dropitem[i], 1, md->mob_id);
 
 			//A Rare Drop Global Announce by Lupus
-			if( mvp_sd && md->db->dropitem[i].rate <= battle_config.rare_drop_announce ) {
+			if (mvp_sd && md->db->dropitem[i].rate <= battle_config.rare_drop_announce) {
 				char message[128];
-				sprintf (message, msg_txt(nullptr,541), mvp_sd->status.name, md->name, it->ename.c_str(), (float)drop_rate/100);
+				sprintf(message, msg_txt(nullptr, 541), mvp_sd->status.name, md->name, it->ename.c_str(), (float)drop_rate / 100);
 				//MSG: "'%s' won %s's %s (chance: %0.02f%%)"
-				intif_broadcast(message,strlen(message)+1,BC_DEFAULT);
+				intif_broadcast(message, strlen(message) + 1, BC_DEFAULT);
 			}
 			// Announce first, or else ditem will be freed. [Lance]
 			// By popular demand, use base drop rate for autoloot code. [Skotlex]
 			mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].rate, homkillonly || merckillonly);
 		}
 
-		// Ore Discovery [Celest]
-		if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rnd()%10000) {
-			struct s_mob_drop mobdrop = {};
-			mobdrop.nameid = itemdb_group.get_random_item_id(IG_FINDINGORE,1);
-			ditem = mob_setdropitem(&mobdrop, 1, md->mob_id);
-			mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, homkillonly || merckillonly);
-		}
-
-		if(sd) {
-			// process script-granted extra drop bonuses
-			t_itemid dropid = 0;
-
-			if (mapdata->getMapFlag(MF_NOBONUSITEMDROP)) {
-				for (const auto &it : sd->add_drop) {
-					if (!&it || (!it.nameid && !it.group))
-						continue;
-					if ((it.race < RC_NONE_ && it.race == -md->mob_id) || //Race < RC_NONE_, use mob_id
-						(it.race == RC_ALL || it.race == status->race) || //Matched race
-						(it.class_ == CLASS_ALL || it.class_ == status->class_)) //Matched class
-					{
-						//Check if the bonus item drop rate should be multiplied with mob level/10 [Lupus]
-						if (it.rate < 0) {
-							//It's negative, then it should be multiplied. with mob_level/10
-							//rate = base_rate * (mob_level/10) + 1
-							drop_rate = (-it.rate) * md->level / 10 + 1;
-							drop_rate = cap_value(drop_rate, max(battle_config.item_drop_adddrop_min,1), min(battle_config.item_drop_adddrop_max,10000));
-						}
-						else
-							//it's positive, then it goes as it is
-							drop_rate = it.rate;
-
-						if (rnd()%10000 >= drop_rate)
-							continue;
-						dropid = (it.nameid > 0) ? it.nameid : itemdb_group.get_random_item_id(it.group,1);
-						struct s_mob_drop mobdrop = {};
-						mobdrop.nameid = dropid;
-
-						mob_item_drop(md, dlist, mob_setdropitem(&mobdrop,1,md->mob_id), 0, drop_rate, homkillonly || merckillonly);
-					}
-				}
-			}
-
-			// process script-granted zeny bonus (get_zeny_num) [Skotlex]
-			if( sd->bonus.get_zeny_num && rnd()%100 < sd->bonus.get_zeny_rate ) {
-				i = sd->bonus.get_zeny_num > 0 ? sd->bonus.get_zeny_num : -md->level * sd->bonus.get_zeny_num;
-				if (!i) i = 1;
-				pc_getzeny(sd, 1+rnd()%i, LOG_TYPE_PICKDROP_MONSTER);
-			}
-		}
-
-		// process items looted by the mob
-		if (md->lootitems) {
-			for (i = 0; i < md->lootitem_count; i++)
-				mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly || merckillonly);
-		}
-
 		// Process map specific drops
 		std::shared_ptr<s_map_drops> mapdrops;
 
@@ -2974,7 +3020,8 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 				if( rnd_chance( it.second->rate, 100000u ) ){
 					// 'Cheat' for autoloot command: rate is changed from n/100000 to n/10000
 					int32 map_drops_rate = max(1, (it.second->rate / 10));
-					mob_item_drop( md, dlist, mob_setdropitem( it.second.get(), 1, md->mob_id ), 0, map_drops_rate, (homkillonly || merckillonly) );
+					std::shared_ptr<s_item_drop> ditem = mob_setdropitem(*it.second, 1, md->mob_id);
+					mob_item_drop( md, dlist, ditem, 0, map_drops_rate, homkillonly || merckillonly );
 				}
 			}
 
@@ -2986,28 +3033,24 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 					if( rnd_chance( it.second->rate, 100000u ) ){
 						// 'Cheat' for autoloot command: rate is changed from n/100000 to n/10000
 						int32 map_drops_rate = max(1, (it.second->rate / 10));
-						mob_item_drop( md, dlist, mob_setdropitem( it.second.get(), 1, md->mob_id ), 0, map_drops_rate, (homkillonly || merckillonly) );
+						std::shared_ptr<s_item_drop> ditem = mob_setdropitem(*it.second, 1, md->mob_id);
+						mob_item_drop( md, dlist, ditem, 0, map_drops_rate, homkillonly || merckillonly );
 					}
 				}
 			}
 		}
 
-		if (dlist->item) //There are drop items.
-			add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr_t)dlist);
-		else //No drops
-			ers_free(item_drop_list_ers, dlist);
-	} else if (md->lootitems && md->lootitem_count) {	//Loot MUST drop!
-		struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list);
-		dlist->m = md->bl.m;
-		dlist->x = md->bl.x;
-		dlist->y = md->bl.y;
-		dlist->first_charid = (mvp_sd ? mvp_sd->status.char_id : 0);
-		dlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
-		dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
-		dlist->item = nullptr;
-		for (i = 0; i < md->lootitem_count; i++)
-			mob_item_drop(md, dlist, mob_setlootitem(&md->lootitems[i], md->mob_id), 1, 10000, homkillonly || merckillonly);
-		add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr_t)dlist);
+		// There are drop items.
+		if (!dlist->items.empty() || !lootlist->items.empty()) {
+			mob_delayed_drops[md->bl.id] = dlist;
+			mob_looted_drops[md->bl.id] = lootlist;
+			add_timer(tick + (!battle_config.delay_battle_damage ? 500 : 0), mob_delay_item_drop, md->bl.id, 0);
+		}
+	}
+	// Loot MUST drop!
+	else if (!lootlist->items.empty()) {
+		mob_looted_drops[md->bl.id] = lootlist;
+		add_timer(tick + (!battle_config.delay_battle_damage ? 500 : 0), mob_delay_item_drop, md->bl.id, 0);
 	}
 
 	if( mvp_sd && md->get_bosstype() == BOSSTYPE_MVP ){
@@ -3106,11 +3149,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 					intif_broadcast(message,strlen(message)+1,BC_DEFAULT);
 				}
 
-				mob_setdropitem_option(&item, &mdrop[i]);
+				mob_setdropitem_option( item, mdrop[i] );
 
 				if((temp = pc_additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) {
 					clif_additem(mvp_sd,0,0,temp);
-					map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd->status.char_id,(second_sd?second_sd->status.char_id:0),(third_sd?third_sd->status.char_id:0),1,0,true);
+					map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd->status.char_id,(second_sd?second_sd->status.char_id:0),(third_sd?third_sd->status.char_id:0),1,0,true,DIR_CENTER);
 				}
 
 				if (i_data->flag.broadcast)
@@ -3196,7 +3239,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		} else if( mvp_sd && !md->state.npc_killmonster ) {
 			pc_setparam(mvp_sd, SP_KILLEDGID, md->bl.id);
 			pc_setparam(mvp_sd, SP_KILLEDRID, md->mob_id);
-			npc_script_event(mvp_sd, NPCE_KILLNPC); // PCKillNPC [Lance]
+			npc_script_event( *mvp_sd, NPCE_KILLNPC );
 		}
 	}
 
@@ -3437,7 +3480,6 @@ int mob_class_change (struct mob_data *md, int mob_id)
 	status_set_viewdata(&md->bl, mob_id);
 	clif_mob_class_change(md,md->vd->class_);
 	status_calc_mob(md,SCO_FIRST);
-	md->ud.state.speed_changed = 1; //Speed change update.
 
 	if (battle_config.monster_class_change_recover) {
 		memset(md->dmglog, 0, sizeof(md->dmglog));
@@ -5301,7 +5343,7 @@ static int mob_read_sqldb(void)
 	for( uint8 fi = 0; fi < ARRAYLENGTH(mob_db_name); ++fi ) {
 		// retrieve all rows from the mob database
 		if( SQL_ERROR == Sql_Query(mmysql_handle, "SELECT `id`,`name_aegis`,`name_english`,`name_japanese`,`level`,`hp`,`sp`,`base_exp`,`job_exp`,`mvp_exp`,`attack`,`attack2`,`defense`,`magic_defense`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`attack_range`,`skill_range`,`chase_range`,`size`,`race`,"
-			"`racegroup_goblin`,`racegroup_kobold`,`racegroup_orc`,`racegroup_golem`,`racegroup_guardian`,`racegroup_ninja`,`racegroup_gvg`,`racegroup_battlefield`,`racegroup_treasure`,`racegroup_biolab`,`racegroup_manuk`,`racegroup_splendide`,`racegroup_scaraba`,`racegroup_ogh_atk_def`,`racegroup_ogh_hidden`,`racegroup_bio5_swordman_thief`,`racegroup_bio5_acolyte_merchant`,`racegroup_bio5_mage_archer`,`racegroup_bio5_mvp`,`racegroup_clocktower`,`racegroup_thanatos`,`racegroup_faceworm`,`racegroup_hearthunter`,`racegroup_rockridge`,`racegroup_werner_lab`,`racegroup_temple_demon`,`racegroup_illusion_vampire`,`racegroup_malangdo`,`racegroup_ep172alpha`,`racegroup_ep172beta`,`racegroup_ep172bath`,`racegroup_illusion_turtle`,`racegroup_rachel_sanctuary`,`racegroup_illusion_luanda`,"
+			"`racegroup_goblin`,`racegroup_kobold`,`racegroup_orc`,`racegroup_golem`,`racegroup_guardian`,`racegroup_ninja`,`racegroup_gvg`,`racegroup_battlefield`,`racegroup_treasure`,`racegroup_biolab`,`racegroup_manuk`,`racegroup_splendide`,`racegroup_scaraba`,`racegroup_ogh_atk_def`,`racegroup_ogh_hidden`,`racegroup_bio5_swordman_thief`,`racegroup_bio5_acolyte_merchant`,`racegroup_bio5_mage_archer`,`racegroup_bio5_mvp`,`racegroup_clocktower`,`racegroup_thanatos`,`racegroup_faceworm`,`racegroup_hearthunter`,`racegroup_rockridge`,`racegroup_werner_lab`,`racegroup_temple_demon`,`racegroup_illusion_vampire`,`racegroup_malangdo`,`racegroup_ep172alpha`,`racegroup_ep172beta`,`racegroup_ep172bath`,`racegroup_illusion_turtle`,`racegroup_rachel_sanctuary`,`racegroup_illusion_luanda`,`racegroup_illusion_frozen`,`racegroup_illusion_moonlight`,"
 			"`element`,`element_level`,`walk_speed`,`attack_delay`,`attack_motion`,`damage_motion`,`damage_taken`,`ai`,`class`,"
 			"`mode_canmove`,`mode_looter`,`mode_aggressive`,`mode_assist`,`mode_castsensoridle`,`mode_norandomwalk`,`mode_nocast`,`mode_canattack`,`mode_castsensorchase`,`mode_changechase`,`mode_angry`,`mode_changetargetmelee`,`mode_changetargetchase`,`mode_targetweak`,`mode_randomtarget`,`mode_ignoremelee`,`mode_ignoremagic`,`mode_ignoreranged`,`mode_mvp`,`mode_ignoremisc`,`mode_knockbackimmune`,`mode_teleportblock`,`mode_fixeditemdrop`,`mode_detector`,`mode_statusimmune`,`mode_skillimmune`,"
 			"`mvpdrop1_item`,`mvpdrop1_rate`,`mvpdrop1_option`,`mvpdrop1_index`,`mvpdrop2_item`,`mvpdrop2_rate`,`mvpdrop2_option`,`mvpdrop2_index`,`mvpdrop3_item`,`mvpdrop3_rate`,`mvpdrop3_option`,`mvpdrop3_index`,"
@@ -6661,8 +6703,8 @@ static void mob_load(void)
 	mob_chat_db.load();	// load before mob_skill_db
 
 	for(int i = 0; i < ARRAYLENGTH(dbsubpath); i++){	
-		int n1 = strlen(db_path)+strlen(dbsubpath[i])+1;
-		int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
+		size_t n1 = strlen( db_path ) + strlen( dbsubpath[i] ) + 1;
+		size_t n2 = strlen( db_path ) + strlen( DBPATH ) + strlen( dbsubpath[i] ) + 1;
 
 		char* dbsubpath1 = (char*)aMalloc(n1+1);
 		char* dbsubpath2 = (char*)aMalloc(n2+1);
@@ -6699,12 +6741,6 @@ static void mob_load(void)
  * Initialize monster data
  */
 void mob_db_load(bool is_reload){
-	if( !is_reload ) {
-		// on mobdbreload it's not neccessary to execute this
-		// item ers needs to be allocated only once
-		item_drop_ers = ers_new(sizeof(struct item_drop),"mob.cpp::item_drop_ers",ERS_OPT_CLEAN);
-		item_drop_list_ers = ers_new(sizeof(struct item_drop_list),"mob.cpp::item_drop_list_ers",ERS_OPT_NONE);
-	}
 	mob_load();
 }
 
@@ -6856,7 +6892,6 @@ void do_final_mob(bool is_reload){
 	mob_summon_db.clear();
 	map_drop_db.clear();
 	if( !is_reload ) {
-		ers_destroy(item_drop_ers);
-		ers_destroy(item_drop_list_ers);
+		mob_delayed_drops.clear();
 	}
 }

+ 9 - 7
src/map/mob.hpp

@@ -469,16 +469,18 @@ enum e_mob_skill_condition {
 };
 
 // The data structures for storing delayed item drops
-struct item_drop {
+struct s_item_drop{
 	struct item item_data;
 	unsigned short mob_id;
 	enum bl_type src_type;
-	struct item_drop* next;
 };
-struct item_drop_list {
-	int16 m, x, y;                       // coordinates
-	int first_charid, second_charid, third_charid; // charid's of players with higher pickup priority
-	struct item_drop* item;            // linked list of drops
+
+struct s_item_drop_list{
+	// coordinates
+	int16 m, x, y;
+	// charid's of players with higher pickup priority
+	int first_charid, second_charid, third_charid;
+	std::vector<std::shared_ptr<s_item_drop>> items;
 };
 
 uint16 mobdb_searchname(const char * const str);
@@ -561,7 +563,7 @@ TIMER_FUNC(mvptomb_delayspawn);
 void mvptomb_create(struct mob_data *md, char *killer, time_t time);
 void mvptomb_destroy(struct mob_data *md);
 
-void mob_setdropitem_option(struct item *itm, struct s_mob_drop *mobdrop);
+void mob_setdropitem_option( item& itm, s_mob_drop& mobdrop );
 
 #define CHK_MOBSIZE(size) ((size) >= SZ_SMALL && (size) < SZ_MAX) /// Check valid Monster Size
 

+ 52 - 53
src/map/npc.cpp

@@ -776,7 +776,7 @@ void BarterDatabase::loadingFinished(){
 		npc_parsename( nd, barter->name.c_str(), nullptr, nullptr, __FILE__ ":" QUOTE(__LINE__) );
 
 		nd->class_ = barter->sprite;
-		nd->speed = 200;
+		nd->speed = DEFAULT_NPC_WALK_SPEED;
 
 		nd->bl.type = BL_NPC;
 		nd->subtype = NPCTYPE_BARTER;
@@ -2229,7 +2229,7 @@ int npc_click(map_session_data* sd, struct npc_data* nd)
 				}
 
 				sd->npc_shopid = nd->bl.id;
-				clif_npc_market_open(sd, nd);
+				clif_npc_market_open( *sd, *nd );
 			}
 #endif
 			break;
@@ -2914,7 +2914,7 @@ e_purchase_result npc_buylist( map_session_data* sd, std::vector<s_npc_buy_list>
 }
 
 /// npc_selllist for script-controlled shops
-static int npc_selllist_sub(map_session_data* sd, int list_length, PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list, struct npc_data* nd)
+static int npc_selllist_sub(map_session_data* sd, int list_length, const PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list, struct npc_data* nd)
 {
 	char npc_ev[EVENT_NAME_LENGTH];
 	char card_slot[NAME_LENGTH];
@@ -2962,28 +2962,24 @@ static int npc_selllist_sub(map_session_data* sd, int list_length, PACKET_CZ_PC_
 
 		script_setarray_pc( sd, "@sold_nameid", i, sd->inventory.u.items_inventory[idx].nameid, &key_nameid );
 		script_setarray_pc( sd, "@sold_quantity", i, item_list[i].amount, &key_amount );
+		script_setarray_pc( sd, "@sold_refine", i, sd->inventory.u.items_inventory[idx].refine, &key_refine );
+		script_setarray_pc( sd, "@sold_attribute", i, sd->inventory.u.items_inventory[idx].attribute, &key_attribute );
+		script_setarray_pc( sd, "@sold_identify", i, sd->inventory.u.items_inventory[idx].identify, &key_identify );
+		script_setarray_pc( sd, "@sold_enchantgrade", i, sd->inventory.u.items_inventory[idx].enchantgrade, &key_enchantgrade );
 
-		if( itemdb_isequip(sd->inventory.u.items_inventory[idx].nameid) )
-		{// process equipment based information into the arrays
-			script_setarray_pc( sd, "@sold_refine", i, sd->inventory.u.items_inventory[idx].refine, &key_refine );
-			script_setarray_pc( sd, "@sold_attribute", i, sd->inventory.u.items_inventory[idx].attribute, &key_attribute );
-			script_setarray_pc( sd, "@sold_identify", i, sd->inventory.u.items_inventory[idx].identify, &key_identify );
-			script_setarray_pc( sd, "@sold_enchantgrade", i, sd->inventory.u.items_inventory[idx].enchantgrade, &key_enchantgrade );
-
-			for( j = 0; j < MAX_SLOTS; j++ )
-			{// store each of the cards from the equipment in the array
-				snprintf(card_slot, sizeof(card_slot), "@sold_card%d", j + 1);
-				script_setarray_pc( sd, card_slot, i, sd->inventory.u.items_inventory[idx].card[j], &key_card[j] );
-			}
+		for( j = 0; j < MAX_SLOTS; j++ )
+		{// store each of the cards from the equipment in the array
+			snprintf(card_slot, sizeof(card_slot), "@sold_card%d", j + 1);
+			script_setarray_pc( sd, card_slot, i, sd->inventory.u.items_inventory[idx].card[j], &key_card[j] );
+		}
 
-			for (j = 0; j < MAX_ITEM_RDM_OPT; j++) { // Store each of the item options in the array
-				snprintf(option_id, sizeof(option_id), "@sold_option_id%d", j + 1);
-				script_setarray_pc( sd, option_id, i, sd->inventory.u.items_inventory[idx].option[j].id, &key_option_id[j] );
-				snprintf(option_val, sizeof(option_val), "@sold_option_val%d", j + 1);
-				script_setarray_pc( sd, option_val, i, sd->inventory.u.items_inventory[idx].option[j].value, &key_option_val[j] );
-				snprintf(option_param, sizeof(option_param), "@sold_option_param%d", j + 1);
-				script_setarray_pc( sd, option_param, i, sd->inventory.u.items_inventory[idx].option[j].param, &key_option_param[j] );
-			}
+		for (j = 0; j < MAX_ITEM_RDM_OPT; j++) { // Store each of the item options in the array
+			snprintf(option_id, sizeof(option_id), "@sold_option_id%d", j + 1);
+			script_setarray_pc( sd, option_id, i, sd->inventory.u.items_inventory[idx].option[j].id, &key_option_id[j] );
+			snprintf(option_val, sizeof(option_val), "@sold_option_val%d", j + 1);
+			script_setarray_pc( sd, option_val, i, sd->inventory.u.items_inventory[idx].option[j].value, &key_option_val[j] );
+			snprintf(option_param, sizeof(option_param), "@sold_option_param%d", j + 1);
+			script_setarray_pc( sd, option_param, i, sd->inventory.u.items_inventory[idx].option[j].param, &key_option_param[j] );
 		}
 	}
 
@@ -2998,7 +2994,7 @@ static int npc_selllist_sub(map_session_data* sd, int list_length, PACKET_CZ_PC_
 ///
 /// @param item_list 'n' pairs <index,amount>
 /// @return result code for clif_parse_NpcSellListSend
-uint8 npc_selllist(map_session_data* sd, int list_length, PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list)
+uint8 npc_selllist(map_session_data* sd, int list_length, const PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list)
 {
 	double z;
 	int i,skill;
@@ -3824,7 +3820,7 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short
 		nd->class_ = JT_GUILD_FLAG;
 	else
 		nd->class_ = JT_WARPNPC;
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 
 	nd->u.warp.mapindex = to_mapindex;
 	nd->u.warp.x = to_x;
@@ -3896,7 +3892,7 @@ static const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const
 		nd->class_ = JT_WARPNPC;
 	else
 		nd->class_ = JT_GUILD_FLAG;
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 
 	nd->u.warp.mapindex = i;
 	nd->u.warp.x = to_x;
@@ -4180,7 +4176,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
 
 	npc_parsename(nd, w3, start, buffer, filepath);
 	nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 
 	++npc_shop;
 	nd->bl.type = BL_NPC;
@@ -4245,7 +4241,6 @@ int npc_convertlabel_db(DBKey key, DBData *data, va_list ap)
 	const char* filepath;
 	struct npc_label_list* label;
 	const char *p;
-	int len;
 
 	nullpo_ret(label_list = va_arg(ap,struct npc_label_list**));
 	nullpo_ret(label_list_num = va_arg(ap,int*));
@@ -4255,7 +4250,8 @@ int npc_convertlabel_db(DBKey key, DBData *data, va_list ap)
 	p = lname;
 	while( ISALNUM(*p) || *p == '_' )
 		++p;
-	len = p-lname;
+
+	size_t len = p - lname;
 
 	// here we check if the label fit into the buffer
 	if( len > NAME_LENGTH )
@@ -4418,7 +4414,7 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons
 
 	npc_parsename(nd, w3, start, buffer, filepath);
 	nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 	nd->u.scr.script = script;
 	nd->u.scr.label_list = label_list;
 	nd->u.scr.label_list_num = label_list_num;
@@ -4557,7 +4553,7 @@ const char* npc_parse_duplicate( char* w1, char* w2, char* w3, char* w4, const c
 	nd = npc_create_npc(m, x, y);
 	npc_parsename(nd, w3, start, buffer, filepath);
 	nd->class_ = m == -1 ? JT_FAKENPC : npc_parseview(w4, start, buffer, filepath);
-	nd->speed = 200;
+	nd->speed = DEFAULT_NPC_WALK_SPEED;
 	nd->src_id = src_id;
 	nd->bl.type = BL_NPC;
 	nd->subtype = (enum npc_subtype)type;
@@ -4690,7 +4686,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) {
 		safestrncpy(wnd->name, "", ARRAYLENGTH(wnd->name));
 		safestrncpy(wnd->exname, newname, ARRAYLENGTH(wnd->exname));
 		wnd->class_ = JT_WARPNPC;
-		wnd->speed = 200;
+		wnd->speed = DEFAULT_NPC_WALK_SPEED;
 		wnd->u.warp.mapindex = map_id2index(imap);
 		wnd->u.warp.x = snd->u.warp.x;
 		wnd->u.warp.y = snd->u.warp.y;
@@ -5323,7 +5319,8 @@ static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const c
 	if (battle_config.force_random_spawn || (mob.x == 0 && mob.y == 0)
 		|| (mob.xs == 1 && mob.ys == 1 && !map_getcell(mob.m, mob.x, mob.y, CELL_CHKREACH)))
 	{	//Force a random spawn anywhere on the map.
-		mob.x = mob.y = 0;
+		// Set x and y to -1 to prevent fallback spawn on cell 0,0
+		mob.x = mob.y = -1;
 		mob.xs = mob.ys = 0;
 	}
 
@@ -5675,28 +5672,34 @@ int npc_parsesrcfile(const char* filepath)
 		// fill w1
 		if( pos[3]-pos[2] > ARRAYLENGTH(w1)-1 )
 			ShowWarning("npc_parsesrcfile: w1 truncated, too much data (%d) in file '%s', line '%d'.\n", pos[3]-pos[2], filepath, strline(buffer,p-buffer));
-		int i = min(pos[3]-pos[2], ARRAYLENGTH(w1)-1);
-		memcpy(w1, p+pos[2], i*sizeof(char));
-		w1[i] = '\0';
+
+		size_t index = std::min( pos[3] - pos[2], ARRAYLENGTH( w1 ) - 1 );
+		memcpy( w1, p + pos[2], index * sizeof( char ) );
+		w1[index] = '\0';
+
 		// fill w2
 		if( pos[5]-pos[4] > ARRAYLENGTH(w2)-1 )
 			ShowWarning("npc_parsesrcfile: w2 truncated, too much data (%d) in file '%s', line '%d'.\n", pos[5]-pos[4], filepath, strline(buffer,p-buffer));
-		i = min(pos[5]-pos[4], ARRAYLENGTH(w2)-1);
-		memcpy(w2, p+pos[4], i*sizeof(char));
-		w2[i] = '\0';
+
+		index = std::min( pos[5] - pos[4], ARRAYLENGTH( w2 ) - 1 );
+		memcpy( w2, p + pos[4], index * sizeof( char ) );
+		w2[index] = '\0';
+
 		// fill w3
 		if( pos[7]-pos[6] > ARRAYLENGTH(w3)-1 )
 			ShowWarning("npc_parsesrcfile: w3 truncated, too much data (%d) in file '%s', line '%d'.\n", pos[7]-pos[6], filepath, strline(buffer,p-buffer));
-		i = min(pos[7]-pos[6], ARRAYLENGTH(w3)-1);
-		memcpy(w3, p+pos[6], i*sizeof(char));
-		w3[i] = '\0';
+
+		index = std::min( pos[7] - pos[6], ARRAYLENGTH( w3 ) - 1 );
+		memcpy( w3, p + pos[6], index * sizeof( char ) );
+		w3[index] = '\0';
+
 		// fill w4 (to end of line)
 		if( pos[1]-pos[8] > ARRAYLENGTH(w4)-1 )
 			ShowWarning("npc_parsesrcfile: w4 truncated, too much data (%d) in file '%s', line '%d'.\n", pos[1]-pos[8], filepath, strline(buffer,p-buffer));
 		if (pos[8] != -1) {
-			i = min(pos[1]-pos[8], ARRAYLENGTH(w4)-1);
-			memcpy(w4, p+pos[8], i*sizeof(char));
-			w4[i] = '\0';
+			index = std::min( pos[1] - pos[8], ARRAYLENGTH( w4 ) - 1 );
+			memcpy( w4, p + pos[8], index * sizeof( char ) );
+			w4[index] = '\0';
 		}
 		else
 			w4[0] = '\0';
@@ -5772,7 +5775,7 @@ int npc_parsesrcfile(const char* filepath)
 			else
 				p = npc_parse_script(w1,w2,w3,w4, p, buffer, filepath);
 		}
-		else if( (i=0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3 )
+		else if( int i = 0; ( sscanf( w2, "duplicate%n", &i ), ( i > 0 && w2[i] == '(' ) ) && count > 3 )
 			p = npc_parse_duplicate(w1,w2,w3,w4, p, buffer, filepath);
 		else if( (strcmpi(w2,"monster") == 0 || strcmpi(w2,"boss_monster") == 0) && count > 3 )
 			p = npc_parse_mob(w1, w2, w3, w4, p, buffer, filepath);
@@ -5788,18 +5791,14 @@ int npc_parsesrcfile(const char* filepath)
 	return 1;
 }
 
-int npc_script_event(map_session_data* sd, enum npce_event type){
+size_t npc_script_event( map_session_data& sd, enum npce_event type ){
 	if (type == NPCE_MAX)
 		return 0;
-	if (!sd) {
-		ShowError("npc_script_event: nullptr sd. Event Type %d\n", type);
-		return 0;
-	}
 
 	std::vector<struct script_event_s>& vector = script_event[type];
 
 	for( struct script_event_s& evt : vector ){
-		npc_event_sub( sd, evt.event, evt.event_name );
+		npc_event_sub( &sd, evt.event, evt.event_name );
 	}
 
 	return vector.size();
@@ -6259,7 +6258,7 @@ void do_init_npc(void){
 	// Init dummy NPC
 	fake_nd = npc_create_npc( -1, 0, 0 );
 	fake_nd->class_ = JT_FAKENPC;
-	fake_nd->speed = 200;
+	fake_nd->speed = DEFAULT_NPC_WALK_SPEED;
 	strcpy(fake_nd->name,"FAKE_NPC");
 	memcpy(fake_nd->exname, fake_nd->name, 9);
 

+ 2 - 2
src/map/npc.hpp

@@ -1581,7 +1581,7 @@ struct npc_data* npc_checknear(map_session_data* sd, struct block_list* bl);
 int npc_buysellsel(map_session_data* sd, int id, int type);
 e_purchase_result npc_buylist(map_session_data* sd, std::vector<s_npc_buy_list>& item_list);
 static int npc_buylist_sub(map_session_data* sd, std::vector<s_npc_buy_list>& item_list, struct npc_data* nd);
-uint8 npc_selllist(map_session_data* sd, int list_length, PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list);
+uint8 npc_selllist(map_session_data* sd, int list_length, const PACKET_CZ_PC_SELL_ITEMLIST_sub* item_list);
 e_purchase_result npc_barter_purchase( map_session_data& sd, std::shared_ptr<s_npc_barter> barter, std::vector<s_barter_purchase>& purchases );
 void npc_parse_mob2(struct spawn_data* mob);
 struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y);
@@ -1632,7 +1632,7 @@ void npc_unload_duplicates (struct npc_data* nd);
 int npc_unload(struct npc_data* nd, bool single);
 int npc_reload(void);
 void npc_read_event_script(void);
-int npc_script_event(map_session_data* sd, enum npce_event type);
+size_t npc_script_event( map_session_data& sd, enum npce_event type );
 
 int npc_duplicate4instance(struct npc_data *snd, int16 m);
 int npc_instanceinit(struct npc_data* nd);

+ 124 - 0
src/map/packets.hpp

@@ -611,6 +611,8 @@ struct PACKET_ZC_HOSKILLINFO_UPDATE {
 } __attribute__((packed));
 DEFINE_PACKET_HEADER(ZC_HOSKILLINFO_UPDATE, 0x239)
 
+// Unused packet (alpha?)
+/*
 struct PACKET_ZC_NOTIFY_MOVE {
 	int16 packetType;
 	uint32 gid;
@@ -618,6 +620,7 @@ struct PACKET_ZC_NOTIFY_MOVE {
 	uint32 moveStartTime;
 } __attribute__((packed));
 DEFINE_PACKET_HEADER(ZC_NOTIFY_MOVE, 0x86)
+*/
 
 struct PACKET_ZC_NOTIFY_PLAYERMOVE {
 	int16 packetType;
@@ -1036,6 +1039,121 @@ struct PACKET_ZC_DELETEITEM_FROM_MCSTORE {
 DEFINE_PACKET_HEADER(ZC_DELETEITEM_FROM_MCSTORE, 0x137);
 #endif
 
+struct PACKET_CZ_REQ_BAN_GUILD{
+	int16 packetType;
+	uint32 guild_id;
+	uint32 AID;
+	uint32 CID;
+	char message[40];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_BAN_GUILD, 0x15b);
+
+struct PACKET_CZ_REQ_LEAVE_GUILD{
+	int16 packetType;
+	uint32 guild_id;
+	uint32 AID;
+	uint32 CID;
+	char message[40];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_LEAVE_GUILD, 0x159);
+
+struct PACKET_CZ_REQ_DISORGANIZE_GUILD{
+	int16 packetType;
+	char key[40];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_DISORGANIZE_GUILD, 0x15d);
+
+struct PACKET_ZC_ACK_DISORGANIZE_GUILD_RESULT{
+	int16 packetType;
+	int32 result;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_ACK_DISORGANIZE_GUILD_RESULT, 0x15e);
+
+struct PACKET_ZC_RESULT_MAKE_GUILD{
+	int16 packetType;
+	uint8 result;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_RESULT_MAKE_GUILD, 0x167);
+
+struct PACKET_CZ_REQ_JOIN_GUILD{
+	int16 packetType;
+	uint32 AID;
+	uint32 inviter_AID;
+	uint32 inviter_CID;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_JOIN_GUILD, 0x168);
+
+struct PACKET_ZC_ACK_REQ_JOIN_GUILD{
+	int16 packetType;
+	uint8 result;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_ACK_REQ_JOIN_GUILD, 0x169);
+
+struct PACKET_ZC_REQ_JOIN_GUILD{
+	int16 packetType;
+	uint32 guild_id;
+	char guild_name[NAME_LENGTH];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_REQ_JOIN_GUILD, 0x16a);
+
+struct PACKET_CZ_JOIN_GUILD{
+	int16 packetType;
+	uint32 guild_id;
+	int32 answer;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_JOIN_GUILD, 0x16b);
+
+struct PACKET_ZC_GUILD_NOTICE{
+	int16 packetType;
+	char subject[60];
+	char notice[120];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_GUILD_NOTICE, 0x16f);
+
+struct PACKET_CZ_REQ_JOIN_GUILD2{
+	int16 packetType;
+	char name[NAME_LENGTH];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_JOIN_GUILD2, 0x916);
+
+struct PACKET_CZ_REQ_JOIN_GROUP{
+	int16 packetType;
+	uint32 AID;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_JOIN_GROUP, 0xfc);
+
+struct PACKET_CZ_JOIN_GROUP{
+	int16 packetType;
+	uint32 party_id;
+	int32 flag;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_JOIN_GROUP, 0xff);
+
+struct PACKET_CZ_REQ_LEAVE_GROUP{
+	int16 packetType;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_LEAVE_GROUP, 0x100);
+
+struct PACKET_CZ_REQ_EXPEL_GROUP_MEMBER{
+	int16 packetType;
+	uint32 AID;
+	char name[NAME_LENGTH];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_REQ_EXPEL_GROUP_MEMBER, 0x103);
+
+struct PACKET_CZ_PARTY_JOIN_REQ{
+	int16 packetType;
+	char name[NAME_LENGTH];
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_PARTY_JOIN_REQ, 0x2c4);
+
+struct PACKET_CZ_PARTY_JOIN_REQ_ACK{
+	int16 packetType;
+	uint32 party_id;
+	uint8 flag;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(CZ_PARTY_JOIN_REQ_ACK, 0x2c7);
+
 // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
 #if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 )
 	#pragma pack( pop )
@@ -1045,6 +1163,7 @@ DEFINE_PACKET_HEADER(ZC_DELETEITEM_FROM_MCSTORE, 0x137);
 DEFINE_PACKET_HEADER(ZC_NOTIFY_CHAT, 0x8d)
 DEFINE_PACKET_HEADER(ZC_ITEM_ENTRY, 0x9d)
 DEFINE_PACKET_HEADER(ZC_MVP_GETTING_ITEM, 0x10a)
+DEFINE_PACKET_HEADER(ZC_MAKABLEITEMLIST, 0x18d)
 DEFINE_PACKET_HEADER(CZ_REQMAKINGITEM, 0x18e)
 DEFINE_PACKET_HEADER(ZC_ACK_REQMAKINGITEM, 0x18f)
 #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
@@ -1059,9 +1178,14 @@ DEFINE_PACKET_HEADER(ZC_PC_CASH_POINT_ITEMLIST, 0x287)
 DEFINE_PACKET_HEADER(ZC_CASH_TIME_COUNTER, 0x298)
 DEFINE_PACKET_HEADER(ZC_CASH_ITEM_DELETE, 0x299)
 DEFINE_PACKET_HEADER(CZ_SKILL_SELECT_RESPONSE, 0x443)
+DEFINE_PACKET_HEADER(ZC_MYITEMLIST_BUYING_STORE, 0x813)
+DEFINE_PACKET_HEADER(ZC_ACK_ITEMLIST_BUYING_STORE, 0x818)
 DEFINE_PACKET_HEADER(ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER, 0x824)
 DEFINE_PACKET_HEADER(CZ_SSILIST_ITEM_CLICK, 0x83c)
 DEFINE_PACKET_HEADER(ZC_ACK_SCHEDULER_CASHITEM, 0x8ca)
+DEFINE_PACKET_HEADER(ZC_NOTIFY_CLAN_CONNECTINFO, 0x988)
+DEFINE_PACKET_HEADER(ZC_ACK_CLAN_LEAVE, 0x989)
+DEFINE_PACKET_HEADER(ZC_NOTIFY_CLAN_CHAT, 0x98e)
 DEFINE_PACKET_HEADER(CZ_REQ_BANKING_DEPOSIT, 0x9a7)
 DEFINE_PACKET_HEADER(CZ_REQ_BANKING_WITHDRAW, 0x9a9)
 DEFINE_PACKET_HEADER(CZ_REQ_BANKING_CHECK, 0x9ab)

+ 187 - 129
src/map/party.cpp

@@ -10,7 +10,7 @@
 #include <common/nullpo.hpp>
 #include <common/random.hpp>
 #include <common/showmsg.hpp>
-#include <common/socket.hpp> // last_tick
+#include <common/socket.hpp> // last_tick, session_isActive
 #include <common/strlib.hpp>
 #include <common/timer.hpp>
 #include <common/utils.hpp>
@@ -40,16 +40,15 @@ int party_create_byscript;
  * Fills the given party_member structure according to the sd provided.
  * Used when creating/adding people to a party. [Skotlex]
  *------------------------------------------*/
-static void party_fill_member(struct party_member* member, map_session_data* sd, unsigned int leader)
-{
-  	member->account_id = sd->status.account_id;
-	member->char_id    = sd->status.char_id;
-	safestrncpy(member->name, sd->status.name, NAME_LENGTH);
-	member->class_     = sd->status.class_;
-	safestrncpy( member->map, mapindex_id2name( sd->mapindex ), sizeof( member->map ) );
-	member->lv         = sd->status.base_level;
-	member->online     = 1;
-	member->leader     = leader;
+static void party_fill_member( struct party_member& member, map_session_data& sd, unsigned int leader ){
+	member.account_id = sd.status.account_id;
+	member.char_id = sd.status.char_id;
+	safestrncpy(member.name, sd.status.name, NAME_LENGTH);
+	member.class_ = sd.status.class_;
+	safestrncpy( member.map, mapindex_id2name( sd.mapindex ), sizeof( member.map ) );
+	member.lv = sd.status.base_level;
+	member.online = 1;
+	member.leader = leader;
 }
 
 /// Get the member_id of a party member.
@@ -142,9 +141,8 @@ struct party_data* party_searchname(const char* str)
 	return p;
 }
 
-int party_create(map_session_data *sd,char *name,int item,int item2)
-{
-	struct party_member leader;
+int party_create( map_session_data& sd, char *name, int item, int item2 ){
+	struct party_member leader = {};
 	char tname[NAME_LENGTH];
 
 	safestrncpy(tname, name, NAME_LENGTH);
@@ -153,14 +151,16 @@ int party_create(map_session_data *sd,char *name,int item,int item2)
 	if( !tname[0] ) // empty name
 		return 0;
 
-	if( sd->status.party_id > 0 || sd->party_joining || sd->party_creating ) { // already associated with a party
-		clif_party_created( *sd, 2 );
+	// already associated with a party
+	if( sd.status.party_id > 0 || sd.party_joining || sd.party_creating ){
+		clif_party_created( sd, 2 );
 		return -2;
 	}
 
-	sd->party_creating = true;
-	party_fill_member(&leader, sd, 1);
+	sd.party_creating = true;
+	party_fill_member( leader, sd, 1 );
 	intif_create_party(&leader,name,item,item2);
+
 	return 1;
 }
 
@@ -202,13 +202,10 @@ int party_request_info(int party_id, uint32 char_id)
  * Close trade window if party member is kicked when trade a party bound item
  * @param sd
  **/
-static void party_trade_bound_cancel(map_session_data *sd) {
+static void party_trade_bound_cancel( map_session_data& sd ){
 #ifdef BOUND_ITEMS
-	nullpo_retv(sd);
-	if (sd->state.isBoundTrading&(1<<BOUND_PARTY))
-		trade_tradecancel(sd);
-#else
-	;
+	if (sd.state.isBoundTrading&(1<<BOUND_PARTY))
+		trade_tradecancel( &sd );
 #endif
 }
 
@@ -383,29 +380,55 @@ int party_recv_info(struct party* sp, uint32 char_id)
 }
 
 ///! TODO: Party invitation cross map-server through inter-server, so does with the reply.
-int party_invite(map_session_data *sd,map_session_data *tsd)
-{
-	struct party_data *p;
-	int i;
+bool party_invite( map_session_data& sd, map_session_data *tsd ){
+	struct party_data* p = party_search( sd.status.party_id );
 
-	nullpo_ret(sd);
+	if( p == nullptr ){
+		return false;
+	}
 
-	if( ( p = party_search(sd->status.party_id) ) == nullptr )
-		return 0;
+	int i;
 
 	// confirm if this player is a party leader
-	ARR_FIND(0, MAX_PARTY, i, p->data[i].sd == sd);
+	ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == &sd );
 
 	if( i == MAX_PARTY || !p->party.member[i].leader ) {
-		clif_displaymessage(sd->fd, msg_txt(sd,282));
-		return 0;
+		clif_displaymessage( sd.fd, msg_txt( &sd, 282 ) );
+		return false;
+	}
+
+	// Party locked.
+	if( map_getmapflag( sd.bl.m, MF_PARTYLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 227 ) );
+		return false;
+	}
+
+	if( p->instance_id > 0 && battle_config.instance_block_invite ){
+		clif_party_invite_reply( sd, "", PARTY_REPLY_MEMORIALDUNGEON );
+		return false;
+	}
+
+	if( tsd == NULL ){
+		clif_party_invite_reply( sd, "", PARTY_REPLY_OFFLINE );
+		return false;
+	}
+
+	if( tsd->status.disable_partyinvite ){
+		clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_JOINMSG_REFUSE );
+		return false;
+	}
+
+	// @noask [LuzZza]
+	if( tsd->state.noask ){
+		clif_noask_sub( sd, *tsd, 394 ); // Autorejected party invite from %s.
+		return false;
 	}
 
-	if (tsd && battle_config.block_account_in_same_party) {
+	if( battle_config.block_account_in_same_party ){
 		ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == tsd->status.account_id);
 		if (i < MAX_PARTY) {
-			clif_party_invite_reply( *sd, tsd->status.name, PARTY_REPLY_DUAL );
-			return 0;
+			clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_DUAL );
+			return false;
 		}
 	}
 
@@ -413,44 +436,39 @@ int party_invite(map_session_data *sd,map_session_data *tsd)
 	ARR_FIND(0, MAX_PARTY, i, p->party.member[i].account_id == 0);
 
 	if( i == MAX_PARTY ) {
-		clif_party_invite_reply( *sd, ( tsd ? tsd->status.name : "" ), PARTY_REPLY_FULL );
-		return 0;
+		clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_FULL );
+		return false;
 	}
 
 	// confirm whether the account has the ability to invite before checking the player
-	if( !pc_has_permission(sd, PC_PERM_PARTY) || (tsd && !pc_has_permission(tsd, PC_PERM_PARTY)) ) {
-		clif_displaymessage(sd->fd, msg_txt(sd,81)); // "Your GM level doesn't authorize you to perform this action on the specified player."
-		return 0;
+	if( !pc_has_permission( &sd, PC_PERM_PARTY ) || !pc_has_permission( tsd, PC_PERM_PARTY ) ) {
+		clif_displaymessage( sd.fd, msg_txt( &sd, 81 ) ); // Your GM level doesn't authorize you to perform this action on the specified player.
+		return false;
 	}
 
-	if( tsd == nullptr) {
-		clif_party_invite_reply( *sd, "", PARTY_REPLY_OFFLINE );
-		return 0;
+	if( !battle_config.invite_request_check && ( tsd->guild_invite > 0 || tsd->trade_partner || tsd->adopt_invite ) ){
+		clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_JOIN_OTHER_PARTY );
+		return false;
 	}
 
-	if(!battle_config.invite_request_check) {
-		if (tsd->guild_invite>0 || tsd->trade_partner || tsd->adopt_invite) {
-			clif_party_invite_reply( *sd, tsd->status.name, PARTY_REPLY_JOIN_OTHER_PARTY );
-			return 0;
-		}
+	// You can't invite someone who has already disconnected.
+	if( !session_isActive( tsd->fd ) ){
+		clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_REJECTED );
+		return false;
 	}
 
-	if (!tsd->fd) { //You can't invite someone who has already disconnected.
-		clif_party_invite_reply( *sd, tsd->status.name, PARTY_REPLY_REJECTED );
-		return 0;
+	// Already associated with a party
+	if( tsd->status.party_id > 0 || tsd->party_invite > 0 ){
+		clif_party_invite_reply( sd, tsd->status.name, PARTY_REPLY_JOIN_OTHER_PARTY );
+		return false;
 	}
 
-	if( tsd->status.party_id > 0 || tsd->party_invite > 0 )
-	{// already associated with a party
-		clif_party_invite_reply( *sd, tsd->status.name, PARTY_REPLY_JOIN_OTHER_PARTY );
-		return 0;
-	}
+	tsd->party_invite = sd.status.party_id;
+	tsd->party_invite_account = sd.status.account_id;
 
-	tsd->party_invite=sd->status.party_id;
-	tsd->party_invite_account=sd->status.account_id;
+	clif_party_invite( sd, *tsd );
 
-	clif_party_invite( *sd, *tsd );
-	return 1;
+	return true;
 }
 
 bool party_isleader( map_session_data* sd ){
@@ -477,16 +495,14 @@ bool party_isleader( map_session_data* sd ){
 	return false;
 }
 
-void party_join( map_session_data* sd, int party_id ){
-	nullpo_retv( sd );
-
+void party_join( map_session_data& sd, int party_id ){
 	// Player is in a party already now
-	if( sd->status.party_id != 0 ){
+	if( sd.status.party_id != 0 ){
 		return;
 	}
 
 	// Player is already associated with a party
-	if( sd->party_creating || sd->party_joining ){
+	if( sd.party_creating || sd.party_joining ){
 		return;
 	}
 
@@ -499,7 +515,7 @@ void party_join( map_session_data* sd, int party_id ){
 	int i;
 
 	if( battle_config.block_account_in_same_party ){
-		ARR_FIND( 0, MAX_PARTY, i, party->party.member[i].account_id == sd->status.account_id );
+		ARR_FIND( 0, MAX_PARTY, i, party->party.member[i].account_id == sd.status.account_id );
 
 		if( i < MAX_PARTY ){
 			// Player is in the party with a different character already
@@ -517,8 +533,8 @@ void party_join( map_session_data* sd, int party_id ){
 
 	struct party_member member = {};
 
-	sd->party_joining = true;
-	party_fill_member( &member, sd, 0 );
+	sd.party_joining = true;
+	party_fill_member( member, sd, 0 );
 	intif_party_addmember( party_id, &member );
 }
 
@@ -552,63 +568,73 @@ bool party_booking_load( uint32 account_id, uint32 char_id, struct s_party_booki
 	return true;
 }
 
-int party_reply_invite(map_session_data *sd,int party_id,int flag)
-{
-	map_session_data* tsd;
-	struct party_member member;
-
-	if( sd->party_invite != party_id ) { // forged
-		sd->party_invite = 0;
-		sd->party_invite_account = 0;
-		return 0;
+bool party_reply_invite( map_session_data& sd, int party_id, int flag ){
+	// forged
+	if( sd.party_invite != party_id ) {
+		sd.party_invite = 0;
+		sd.party_invite_account = 0;
+		return false;
 	}
 
 	// The character is already in a party, possibly left a party invite open and created his own party
-	if( sd->status.party_id != 0 ){
+	if( sd.status.party_id != 0 ){
 		// On Aegis no rejection packet is sent to the inviting player
-		sd->party_invite = 0;
-		sd->party_invite_account = 0;
-		return 0;
+		sd.party_invite = 0;
+		sd.party_invite_account = 0;
+		return false;
 	}
 
-	tsd = map_id2sd(sd->party_invite_account);
+	// accepted and allowed
+	if( flag == 1 && !sd.party_creating && !sd.party_joining ) {
+		struct party_data* party = party_search( party_id );
 
-	if( flag == 1 && !sd->party_creating && !sd->party_joining ) { // accepted and allowed
-		sd->party_joining = true;
-		party_fill_member(&member, sd, 0);
-		intif_party_addmember(sd->party_invite, &member);
-		return 1;
-	} else { // rejected or failure
-		sd->party_invite = 0;
-		sd->party_invite_account = 0;
+		if( party && party->instance_id > 0 && battle_config.instance_block_invite ){
+			sd.party_invite = 0;
+			sd.party_invite_account = 0;
+			return false;
+		}
+
+		struct party_member member = {};
 
-		if( tsd != nullptr )
-			clif_party_invite_reply( *tsd, sd->status.name, PARTY_REPLY_REJECTED );
+		sd.party_joining = true;
+		party_fill_member( member, sd, 0 );
+		intif_party_addmember( sd.party_invite, &member );
+
+		return true;
 	}
 
-	return 0;
+	// rejected or failure
+	map_session_data* tsd = map_id2sd( sd.party_invite_account );
+
+	sd.party_invite = 0;
+	sd.party_invite_account = 0;
+
+	if( tsd != nullptr ) {
+		clif_party_invite_reply( *tsd, sd.status.name, PARTY_REPLY_REJECTED );
+	}
+
+	return false;
 }
 
 //Invoked when a player joins:
 //- Loads up party data if not in server
 //- Sets up the pointer to him
 //- Player must be authed/active and belong to a party before calling this method
-void party_member_joined(map_session_data *sd)
-{
-	struct party_data* p = party_search(sd->status.party_id);
+void party_member_joined( map_session_data& sd ){
+	struct party_data* p = party_search( sd.status.party_id );
 	int i;
 
 	if (!p) {
-		party_request_info(sd->status.party_id, sd->status.char_id);
+		party_request_info( sd.status.party_id, sd.status.char_id );
 		return;
 	}
 
-	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id );
+	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd.status.account_id && p->party.member[i].char_id == sd.status.char_id );
 
 	if (i < MAX_PARTY) {
-		p->data[i].sd = sd;
+		p->data[i].sd = &sd;
 	} else
-		sd->status.party_id = 0; //He does not belongs to the party really?
+		sd.status.party_id = 0; //He does not belongs to the party really?
 }
 
 /// Invoked (from char-server) when a new member is added to the party.
@@ -670,31 +696,39 @@ int party_member_added(int party_id,uint32 account_id,uint32 char_id, int flag)
 }
 
 /// Party member 'sd' requesting kick of member with <account_id, name>.
-int party_removemember(map_session_data* sd, uint32 account_id, char* name)
-{
-	struct party_data *p;
-	int i;
+bool party_removemember( map_session_data& sd, uint32 account_id, const char* name ){
+	// Party locked.
+	if( map_getmapflag( sd.bl.m, MF_PARTYLOCK ) ){
+		clif_displaymessage( sd.fd, msg_txt( &sd, 227 ) );
+		return false;
+	}
 
-	p = party_search(sd->status.party_id);
+	struct party_data* p = party_search( sd.status.party_id );
 
 	if( p == nullptr )
-		return 0;
+		return false;
+
+	if( p->instance_id > 0 && battle_config.instance_block_expulsion ){
+		return false;
+	}
+
+	int i;
 
 	// check the requesting char's party membership
-	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id );
+	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd.status.account_id && p->party.member[i].char_id == sd.status.char_id );
 	if( i == MAX_PARTY )
-		return 0; // request from someone not in party? o.O
+		return false; // request from someone not in party? o.O
 	if( !p->party.member[i].leader )
-		return 0; // only party leader may remove members
+		return false; // only party leader may remove members
 
 	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && strncmp(p->party.member[i].name,name,NAME_LENGTH) == 0 );
 	if( i == MAX_PARTY )
-		return 0; // no such char in party
+		return false; // no such char in party
 
 	party_trade_bound_cancel(sd);
 	intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id,p->party.member[i].name,PARTY_MEMBER_WITHDRAW_EXPEL);
 
-	return 1;
+	return true;
 }
 
 int party_removemember2(map_session_data *sd,uint32 char_id,int party_id)
@@ -703,7 +737,7 @@ int party_removemember2(map_session_data *sd,uint32 char_id,int party_id)
 		if( !sd->status.party_id )
 			return -3;
 
-		party_trade_bound_cancel(sd);
+		party_trade_bound_cancel( *sd );
 		intif_party_leave(sd->status.party_id,sd->status.account_id,sd->status.char_id,sd->status.name,PARTY_MEMBER_WITHDRAW_EXPEL);
 		return 1;
 	} else {
@@ -722,23 +756,44 @@ int party_removemember2(map_session_data *sd,uint32 char_id,int party_id)
 }
 
 /// Party member 'sd' requesting exit from party.
-int party_leave(map_session_data *sd)
-{
-	struct party_data *p;
+bool party_leave( map_session_data& sd, bool showMessage ){
+	// Party locked.
+	if( map_getmapflag( sd.bl.m, MF_PARTYLOCK ) ){
+		// If it was not triggered by the user itself, but from a script for example
+		if( showMessage ){
+			clif_displaymessage( sd.fd, msg_txt( &sd,227 ) );
+		}
+
+		return false;
+	}
+
+	struct party_data* p = party_search( sd.status.party_id );
+
+	if( p == nullptr ){
+		return false;
+	}
+
+	if( p->instance_id > 0 && battle_config.instance_block_leave ){
+		// If it was not triggered by the user itself, but from a script for example
+		if( showMessage ){
+			clif_party_withdraw( sd, sd.status.account_id, sd.status.name, PARTY_MEMBER_WITHDRAW_CANT_LEAVE, SELF );
+		}
+
+		return false;
+	}
+
 	int i;
 
-	p = party_search(sd->status.party_id);
+	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd.status.account_id && p->party.member[i].char_id == sd.status.char_id );
 
-	if( p == nullptr )
-		return 0;
+	if( i == MAX_PARTY ){
+		return false;
+	}
 
-	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id );
-	if( i == MAX_PARTY )
-		return 0;
+	party_trade_bound_cancel( sd );
+	intif_party_leave( p->party.party_id, sd.status.account_id, sd.status.char_id, sd.status.name, PARTY_MEMBER_WITHDRAW_LEAVE );
 
-	party_trade_bound_cancel(sd);
-	intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id,sd->status.name,PARTY_MEMBER_WITHDRAW_LEAVE);
-	return 1;
+	return true;
 }
 
 /// Invoked (from char-server) when a party member leaves the party.
@@ -769,7 +824,7 @@ int party_member_withdraw(int party_id, uint32 account_id, uint32 char_id, char
 		int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion
 		int j,i;
 
-		party_trade_bound_cancel(sd);
+		party_trade_bound_cancel( *sd );
 		j = pc_bound_chk(sd,BOUND_PARTY,idxlist);
 
 		for(i = 0; i < j; i++)
@@ -903,6 +958,10 @@ int party_changeleader(map_session_data *sd, map_session_data *tsd, struct party
 		if ((p = party_search(sd->status.party_id)) == nullptr )
 			return -1;
 
+		if( p->instance_id > 0 && battle_config.instance_block_leaderchange ){
+			return 0;
+		}
+
 		ARR_FIND( 0, MAX_PARTY, mi, p->data[mi].sd == sd );
 		if (mi == MAX_PARTY)
 			return 0; // Shouldn't happen
@@ -1052,8 +1111,7 @@ int party_send_message(map_session_data *sd,const char *mes, size_t len)
 	return 0;
 }
 
-int party_recv_message(int party_id,uint32 account_id,const char *mes,int len)
-{
+int party_recv_message( int party_id, uint32 account_id, const char *mes, size_t len ){
 	struct party_data *p;
 	if( (p=party_search(party_id))==nullptr)
 		return 0;

+ 8 - 8
src/map/party.hpp

@@ -62,20 +62,20 @@ struct party_data* party_searchname(const char* str);
 int party_getmemberid(struct party_data* p, map_session_data* sd);
 map_session_data* party_getavailablesd(struct party_data *p);
 
-int party_create(map_session_data *sd,char *name, int item, int item2);
+int party_create( map_session_data& sd, char *name, int item, int item2 );
 void party_created(uint32 account_id,uint32 char_id,int fail,int party_id,char *name);
 int party_request_info(int party_id, uint32 char_id);
-int party_invite(map_session_data *sd,map_session_data *tsd);
-void party_member_joined(map_session_data *sd);
+bool party_invite( map_session_data& sd, map_session_data* tsd );
+void party_member_joined( map_session_data& sd );
 int party_member_added(int party_id,uint32 account_id,uint32 char_id,int flag);
-int party_leave(map_session_data *sd);
-int party_removemember(map_session_data *sd,uint32 account_id,char *name);
+bool party_leave( map_session_data& sd, bool showMessage = false );
+bool party_removemember( map_session_data& sd, uint32 account_id, const char *name );
 int party_removemember2(map_session_data *sd,uint32 char_id,int party_id);
 int party_member_withdraw(int party_id, uint32 account_id, uint32 char_id, char *name, enum e_party_member_withdraw type);
 bool party_isleader( map_session_data* sd );
-void party_join( map_session_data* sd, int party_id );
+void party_join( map_session_data& sd, int party_id );
 bool party_booking_load( uint32 account_id, uint32 char_id, struct s_party_booking_requirement* booking );
-int party_reply_invite(map_session_data *sd,int party_id,int flag);
+bool party_reply_invite( map_session_data& sd, int party_id, int flag );
 #define party_add_member(party_id,sd) party_reply_invite(sd,party_id,1)
 int party_recv_noinfo(int party_id, uint32 char_id);
 int party_recv_info(struct party* sp, uint32 char_id);
@@ -89,7 +89,7 @@ void party_send_movemap(map_session_data *sd);
 void party_send_levelup(map_session_data *sd);
 int party_send_logout(map_session_data *sd);
 int party_send_message(map_session_data *sd,const char *mes, size_t len);
-int party_recv_message(int party_id,uint32 account_id,const char *mes,int len);
+int party_recv_message( int party_id, uint32 account_id, const char *mes, size_t len );
 int party_skill_check(map_session_data *sd, int party_id, uint16 skill_id, uint16 skill_lv);
 int party_send_xy_clear(struct party_data *p);
 void party_exp_share(struct party_data *p,struct block_list *src,t_exp base_exp,t_exp job_exp,int zeny);

+ 53 - 23
src/map/pc.cpp

@@ -2395,11 +2395,11 @@ void pc_reg_received(map_session_data *sd)
 	}
 
 	if (sd->status.party_id > 0)
-		party_member_joined(sd);
+		party_member_joined( *sd );
 	if (sd->status.guild_id > 0)
 		guild_member_joined(sd);
 	if (sd->status.clan_id > 0)
-		clan_member_joined(sd);
+		clan_member_joined( *sd );
 #if !( PACKETVER_MAIN_NUM >= 20190403 || PACKETVER_RE_NUM >= 20190320 || PACKETVER_ZERO_NUM >= 20190410 )
 	// Before those clients you could send out the instance info even when the client was still loading the map, afterwards you need to send it later
 	clif_instance_info( *sd );
@@ -6063,9 +6063,12 @@ bool pc_dropitem(map_session_data *sd,int n,int amount)
 		return false;
 	}
 
-	// bypass drop restriction in map_addflooritem because we've already checked it above
-	if (!map_addflooritem(&sd->inventory.u.items_inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2|4, 0))
+	// Bypass drop restriction in map_addflooritem because we've already checked it above
+	if (!map_addflooritem(&sd->inventory.u.items_inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2|4, 0,
+		false, DIR_MAX, battle_config.item_stacking?BL_NUL:BL_ITEM))
+	{
 		return false;
+	}
 
 	pc_delitem(sd, n, amount, 1, 0, LOG_TYPE_PICKDROP_PLAYER);
 	clif_dropitem( *sd, n, amount );
@@ -6270,14 +6273,35 @@ bool pc_isUseitem(map_session_data *sd,int n)
 	if( itemdb_group.item_exists(IG_MERCENARY, nameid) && sd->md != nullptr )
 		return false; // Mercenary Scrolls
 
-	if( item->flag.group || item->type == IT_CASH) {	//safe check type cash disappear when overweight [Napster]
-		if( pc_is90overweight(sd) ) {
-			clif_msg(sd, MSI_CANT_GET_ITEM_BECAUSE_WEIGHT);
-			return false;
+	// Safe check type cash disappear when overweight [Napster]
+	if( item->flag.group || item->type == IT_CASH ){
+#ifdef RENEWAL
+		// Check if the player is not overweighted
+		// In Renewal the limit is 70% weight and gives the same error message
+		if (pc_is70overweight(sd)) {
+			clif_msg_color(sd, MSI_PICKUP_FAILED_ITEMCREATE, color_table[COLOR_RED]);
+			return 0;
 		}
-		if( !pc_inventoryblank(sd) ) {
-			clif_messagecolor(&sd->bl, color_table[COLOR_RED], msg_txt(sd, 732), false, SELF); //Item cannot be open when inventory is full
-			return false;
+#else
+		// Check if the player is not overweighted
+		// In Pre-Renewal the limit is 50% weight and gives a specific error message
+		if (pc_is50overweight(sd)) {
+			clif_msg_color(sd, MSI_CANT_GET_ITEM_BECAUSE_WEIGHT, color_table[COLOR_RED]);
+			return 0;
+		}
+#endif
+
+		// Check if the player has enough free spaces in the inventory
+		// Official servers use 10 as the minimum amount of slots required to get the items
+		// The <= is intentional, as in official servers you actually need an extra empty slot
+		// TODO: Count the items the player will get and check for the actual inventory space required std::max<size_t>( count, 10 )
+		if (pc_inventoryblank(sd) <= 10) {
+#ifdef RENEWAL
+			clif_msg_color(sd, MSI_PICKUP_FAILED_ITEMCREATE, color_table[COLOR_RED]);
+#else
+			clif_msg_color(sd, MSI_CANT_GET_ITEM_BECAUSE_COUNT, color_table[COLOR_RED]);
+#endif
+			return 0;
 		}
 	}
 
@@ -6714,7 +6738,7 @@ bool pc_steal_item(map_session_data *sd,struct block_list *bl, uint16 skill_lv)
 	tmp_item.amount = 1;
 	tmp_item.identify = itemdb_isidentified(itemid);
 	if( battle_config.skill_steal_random_options ){
-		mob_setdropitem_option( &tmp_item, &md->db->dropitem[i] );
+		mob_setdropitem_option( tmp_item, md->db->dropitem[i] );
 	}
 	flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER);
 
@@ -6915,7 +6939,8 @@ enum e_setpos pc_setpos(map_session_data* sd, unsigned short mapindex, int x, in
 		if (sd->state.buyingstore) // Stop buyingstore
 			buyingstore_close(sd);
 
-		npc_script_event(sd, NPCE_LOGOUT);
+		npc_script_event( *sd, NPCE_LOGOUT );
+
 		//remove from map, THEN change x/y coordinates
 		unit_remove_map_pc(sd,clrtype);
 		sd->mapindex = mapindex;
@@ -8133,7 +8158,7 @@ int pc_checkbaselevelup(map_session_data *sd) {
 		}
 	}
 	clif_misceffect( sd->bl, NOTIFYEFFECT_BASE_LEVEL_UP );
-	npc_script_event(sd, NPCE_BASELVUP); //LORDALFA - LVLUPEVENT
+	npc_script_event( *sd, NPCE_BASELVUP );
 
 	if(sd->status.party_id)
 		party_send_levelup(sd);
@@ -8191,7 +8216,8 @@ int pc_checkjoblevelup(map_session_data *sd)
 	if (pc_checkskill(sd, SG_DEVIL) && ((sd->class_&MAPID_THIRDMASK) == MAPID_STAR_EMPEROR || pc_is_maxjoblv(sd)) )
 		clif_status_change(&sd->bl, EFST_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL.
 
-	npc_script_event(sd, NPCE_JOBLVUP);
+	npc_script_event( *sd, NPCE_JOBLVUP );
+
 	for (; job_level <= sd->status.job_level; job_level++)
 		achievement_update_objective(sd, AG_GOAL_LEVEL, 1, job_level);
 
@@ -9551,8 +9577,12 @@ void pc_damage(map_session_data *sd,struct block_list *src,unsigned int hp, unsi
 }
 
 TIMER_FUNC(pc_close_npc_timer){
-	TBL_PC *sd = map_id2sd(id);
-	if(sd) pc_close_npc(sd,data);
+	map_session_data* sd = map_id2sd( id );
+
+	if( sd != nullptr ){
+		pc_close_npc( sd, static_cast<int>( data ) );
+	}
+
 	return 0;
 }
 /**
@@ -9778,7 +9808,7 @@ int pc_dead(map_session_data *sd,struct block_list *src)
 	if (src && src->type == BL_PC) {
 		map_session_data *ssd = (map_session_data *)src;
 		pc_setparam(ssd, SP_KILLEDRID, sd->bl.id);
-		npc_script_event(ssd, NPCE_KILLPC);
+		npc_script_event( *ssd, NPCE_KILLPC );
 
 		if (battle_config.pk_mode&2) {
 			ssd->status.manner -= 5;
@@ -9821,7 +9851,7 @@ int pc_dead(map_session_data *sd,struct block_list *src)
 		item_tmp.card[1]=0;
 		item_tmp.card[2]=GetWord(sd->status.char_id,0); // CharId
 		item_tmp.card[3]=GetWord(sd->status.char_id,1);
-		map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+		map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 	}
 
 	//Remove bonus_script when dead
@@ -10602,7 +10632,7 @@ int pc_itemheal(map_session_data *sd, t_itemid itemid, int hp, int sp)
 		}
 
 #ifdef RENEWAL
-		if (sd->sc.getSCE(SC_EXTREMITYFIST2))
+		if (sd->sc.getSCE(SC_EXTREMITYFIST))
 			sp = 0;
 #endif
 		if (sd->sc.getSCE(SC_BITESCAR))
@@ -15424,7 +15454,7 @@ static void pc_macro_punishment(map_session_data &sd, e_macro_detect_status styp
  * @param image_size: Captcha image size
  * @param captcha_answer: Answer to captcha
  */
-void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, char captcha_answer[CAPTCHA_ANSWER_SIZE]) {
+void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, const char captcha_answer[CAPTCHA_ANSWER_SIZE]) {
 	nullpo_retv(captcha_answer);
 
 	sd.captcha_upload.cd = nullptr;
@@ -15453,7 +15483,7 @@ void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, char cap
  * @param upload_size: Captcha size
  * @param upload_data: Image data
  */
-void pc_macro_captcha_register_upload(map_session_data &sd, uint16 upload_size, char *upload_data) {
+void pc_macro_captcha_register_upload(map_session_data &sd, uint16 upload_size, const char *upload_data) {
 	nullpo_retv(upload_data);
 
 	memcpy(&sd.captcha_upload.cd->image_data[sd.captcha_upload.upload_size], upload_data, upload_size);
@@ -15520,7 +15550,7 @@ TIMER_FUNC(pc_macro_detector_timeout) {
  * @param sd: Player data
  * @param captcha_answer: Captcha answer entered by player
  */
-void pc_macro_detector_process_answer(map_session_data &sd, char captcha_answer[CAPTCHA_ANSWER_SIZE]) {
+void pc_macro_detector_process_answer(map_session_data &sd, const char captcha_answer[CAPTCHA_ANSWER_SIZE]) {
 	nullpo_retv(captcha_answer);
 
 	const std::shared_ptr<s_captcha_data> cd = sd.macro_detect.cd;

+ 5 - 4
src/map/pc.hpp

@@ -446,7 +446,8 @@ public:
 		bool barter_open;
 		bool barter_extended_open;
 		bool enchantgrade_open; // Whether the enchantgrade window is open or not
-		unsigned int block_action : 10;
+		// Bitmask of e_pcblock_action_flag values
+		uint16 block_action;
 		bool refineui_open;
 		t_itemid inventory_expansion_confirmation;
 		uint16 inventory_expansion_amount;
@@ -1732,12 +1733,12 @@ void pc_attendance_claim_reward( map_session_data* sd );
 void pc_jail(map_session_data &sd, int32 duration = INT_MAX);
 
 // Captcha Register
-void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, char captcha_answer[CAPTCHA_ANSWER_SIZE]);
-void pc_macro_captcha_register_upload(map_session_data & sd, uint16 upload_size, char *upload_data);
+void pc_macro_captcha_register(map_session_data &sd, uint16 image_size, const char captcha_answer[CAPTCHA_ANSWER_SIZE]);
+void pc_macro_captcha_register_upload(map_session_data & sd, uint16 upload_size, const char *upload_data);
 
 // Macro Detector
 TIMER_FUNC(pc_macro_detector_timeout);
-void pc_macro_detector_process_answer(map_session_data &sd, char captcha_answer[CAPTCHA_ANSWER_SIZE]);
+void pc_macro_detector_process_answer(map_session_data &sd, const char captcha_answer[CAPTCHA_ANSWER_SIZE]);
 void pc_macro_detector_disconnect(map_session_data &sd);
 
 // Macro Reporter

+ 71 - 69
src/map/pet.cpp

@@ -521,7 +521,7 @@ void pet_clear_support_bonuses(map_session_data *sd) {
 	}
 
 	if (pd->loot) {
-		pet_lootitem_drop(pd, sd);
+		pet_lootitem_drop( *pd, sd );
 
 		if (pd->loot->item)
 			aFree(pd->loot->item);
@@ -579,8 +579,7 @@ bool PetDatabase::reload(){
 
 PetDatabase pet_db;
 
-static struct eri *item_drop_ers; //For loot drops delay structures.
-static struct eri *item_drop_list_ers;
+std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> pet_delayed_drops;
 
 /**
  * Get the value of the pet's hunger.
@@ -942,7 +941,7 @@ static int pet_performance(map_session_data *sd, struct pet_data *pd)
 
 	pet_stop_walking(pd,2000<<8);
 	clif_pet_performance(pd, rnd_value(1, val));
-	pet_lootitem_drop(pd,nullptr);
+	pet_lootitem_drop( *pd, nullptr );
 
 	return 1;
 }
@@ -954,7 +953,7 @@ static int pet_performance(map_session_data *sd, struct pet_data *pd)
  * @return true if everything went well, false if the egg is not found in the inventory.
  */
 bool pet_return_egg( map_session_data *sd, struct pet_data *pd ){
-	pet_lootitem_drop(pd,sd);
+	pet_lootitem_drop( *pd, sd );
 
 	int i = pet_egg_search( sd, pd->pet.pet_id );
 
@@ -1279,7 +1278,6 @@ int pet_catch_process2(map_session_data* sd, int target_id)
 	}
 
 	if(sd->catch_target_class != md->mob_id || !pet) {
-		clif_emotion(&md->bl, ET_ANGER);	//mob will do /ag if wrong lure is used on them.
 		clif_pet_roulette( *sd, false );
 		sd->catch_target_class = PET_CATCH_FAIL;
 
@@ -1293,6 +1291,14 @@ int pet_catch_process2(map_session_data* sd, int target_id)
 		return 1;
 	}
 
+	if (!pc_inventoryblank(sd)) {
+		clif_pet_roulette(*sd, false);
+		sd->catch_target_class = PET_CATCH_FAIL;
+		clif_msg_color(sd, MSI_CANT_GET_ITEM_BECAUSE_COUNT, color_table[COLOR_RED]);
+
+		return 1;
+	}
+
 	status_change* tsc = status_get_sc( &md->bl );
 
 	if( battle_config.pet_hide_check && tsc && ( tsc->getSCE(SC_HIDING) || tsc->getSCE(SC_CLOAKING) || tsc->getSCE(SC_CAMOUFLAGE) || tsc->getSCE(SC_NEWMOON) || tsc->getSCE(SC_CLOAKINGEXCEED) ) ){
@@ -1377,8 +1383,10 @@ bool pet_get_egg(uint32 account_id, short pet_class, int pet_id ) {
 	tmp_item.card[3] |= pet_get_card3_intimacy( pet->intimate ); // Store intimacy status based on initial intimacy
 
 	if((ret = pc_additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER))) {
-		clif_additem(sd,0,0,ret);
-		map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+		clif_additem(sd, 0, 0, ret);
+		intif_delete_petdata(pet_id);
+
+		return false;
 	}
 
 	return true;
@@ -1555,18 +1563,27 @@ static int pet_unequipitem(map_session_data *sd, struct pet_data *pd)
 		return 1;
 
 	t_itemid nameid = pd->pet.equip;
-	pd->pet.equip = 0;
-	status_set_viewdata(&pd->bl, pd->pet.class_);
-	clif_pet_equip_area(pd);
 	memset(&tmp_item,0,sizeof(tmp_item));
 	tmp_item.nameid = nameid;
 	tmp_item.identify = 1;
 
 	if((flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_OTHER))) {
-		clif_additem(sd,0,0,flag);
-		map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+		clif_additem(sd, 0, 0, flag);
+
+		// On official servers the item is destroyed if you don't have enough space
+		if (battle_config.pet_unequip_destroy) {
+			log_pick_pc( sd, LOG_TYPE_OTHER, -1, &tmp_item );
+		}
+		// Don't unequip (and don't destroy) the item if failed to add it to the inventory
+		else {
+			return 1;
+		}
 	}
 
+	pd->pet.equip = 0;
+	status_set_viewdata(&pd->bl, pd->pet.class_);
+	clif_pet_equip_area(pd);
+
 	if( battle_config.pet_equip_required ) { // Skotlex: halt support timers if needed
 		if( pd->state.skillbonus ) {
 			pd->state.skillbonus = 0;
@@ -1768,7 +1785,7 @@ static int pet_ai_sub_hard(struct pet_data *pd, map_session_data *sd, t_tick tic
 			return 0; // Wait until the pet finishes walking back to master.
 
 		pd->status.speed = pd->get_pet_walk_speed();
-		pd->ud.state.change_walk_target = pd->ud.state.speed_changed = 1;
+		pd->ud.state.change_walk_target = 1;
 	}
 
 	if (pd->target_id) {
@@ -1912,24 +1929,20 @@ static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
  * @return 0
  */
 static TIMER_FUNC(pet_delay_item_drop){
-	struct item_drop_list *list;
-	struct item_drop *ditem;
+	uint32 bl_id = static_cast<uint32>( id );
+	std::shared_ptr<s_item_drop_list> list = util::umap_find( pet_delayed_drops, bl_id );
 
-	list = (struct item_drop_list *)data;
-	ditem = list->item;
-
-	while (ditem) {
-		struct item_drop *ditem_prev;
+	if( list == nullptr ){
+		return 0;
+	}
 
+	for( std::shared_ptr<s_item_drop>& ditem : list->items ){
 		map_addflooritem(&ditem->item_data,ditem->item_data.amount,
 			list->m,list->x,list->y,
 			list->first_charid,list->second_charid,list->third_charid,4,0);
-		ditem_prev = ditem;
-		ditem = ditem->next;
-		ers_free(item_drop_ers, ditem_prev);
 	}
 
-	ers_free(item_drop_list_ers, list);
+	pet_delayed_drops.erase( bl_id );
 
 	return 0;
 }
@@ -1938,61 +1951,54 @@ static TIMER_FUNC(pet_delay_item_drop){
  * Make a pet drop their looted items.
  * @param pd : pet requesting
  * @param sd : player requesting
- * @return 1:success, 0:failure
  */
-int pet_lootitem_drop(struct pet_data *pd,map_session_data *sd)
-{
-	int i;
-	struct item_drop_list *dlist;
-	struct item_drop *ditem;
+void pet_lootitem_drop( pet_data& pd, map_session_data* sd ){
+	if( !pd.loot || !pd.loot->count ){
+		return;
+	}
 
-	if(!pd || !pd->loot || !pd->loot->count)
-		return 0;
+	std::shared_ptr<s_item_drop_list> dlist = std::make_shared<s_item_drop_list>();
 
-	dlist = ers_alloc(item_drop_list_ers, struct item_drop_list);
-	dlist->m = pd->bl.m;
-	dlist->x = pd->bl.x;
-	dlist->y = pd->bl.y;
+	dlist->m = pd.bl.m;
+	dlist->x = pd.bl.x;
+	dlist->y = pd.bl.y;
 	dlist->first_charid = 0;
 	dlist->second_charid = 0;
 	dlist->third_charid = 0;
-	dlist->item = nullptr;
-
-	for(i = 0; i < pd->loot->count; i++) {
-		struct item *it;
 
-		it = &pd->loot->item[i];
+	for( int i = 0; i < pd.loot->count; i++) {
+		struct item* it = &pd.loot->item[i];
 
-		if(sd){
-			unsigned char flag = 0;
+		if( sd != nullptr ){
+			unsigned char flag = pc_additem( sd, it, it->amount, LOG_TYPE_PICKDROP_PLAYER );
 
-			if((flag = pc_additem(sd,it,it->amount,LOG_TYPE_PICKDROP_PLAYER))){
-				clif_additem(sd,0,0,flag);
-				ditem = ers_alloc(item_drop_ers, struct item_drop);
-				memcpy(&ditem->item_data, it, sizeof(struct item));
-				ditem->next = dlist->item;
-				dlist->item = ditem;
+			if( flag == ADDITEM_SUCCESS ){
+				continue;
 			}
-		} else {
-			ditem = ers_alloc(item_drop_ers, struct item_drop);
-			memcpy(&ditem->item_data, it, sizeof(struct item));
-			ditem->next = dlist->item;
-			dlist->item = ditem;
+
+			// Inform client about failure to add
+			clif_additem( sd, 0, 0, flag );
 		}
+
+		// Store the drop for later
+		std::shared_ptr<s_item_drop> ditem = std::make_shared<s_item_drop>();
+
+		memcpy( &ditem->item_data, it, sizeof( struct item ) );
+
+		dlist->items.push_back( ditem );
 	}
 
 	//The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori)
-	memset(pd->loot->item,0,pd->loot->max * sizeof(struct item));
-	pd->loot->count = 0;
-	pd->loot->weight = 0;
-	pd->ud.canact_tick = gettick()+10000;	//prevent picked up during 10*1000ms
+	memset( pd.loot->item, 0, pd.loot->max * sizeof( struct item ) );
+	pd.loot->count = 0;
+	pd.loot->weight = 0;
+	pd.ud.canact_tick = gettick()+10000;	//prevent picked up during 10*1000ms
 
-	if (dlist->item)
-		add_timer(gettick()+540,pet_delay_item_drop,0,(intptr_t)dlist);
-	else
-		ers_free(item_drop_list_ers, dlist);
+	if( !dlist->items.empty() ){
+		pet_delayed_drops[pd.bl.id] = dlist;
 
-	return 1;
+		add_timer( gettick() + 500, pet_delay_item_drop, pd.bl.id, 0 );
+	}
 }
 
 /**
@@ -2458,9 +2464,6 @@ void do_init_pet(void)
 {
 	pet_db.load();
 
-	item_drop_ers = ers_new(sizeof(struct item_drop),"pet.cpp::item_drop_ers",ERS_OPT_NONE);
-	item_drop_list_ers = ers_new(sizeof(struct item_drop_list),"pet.cpp::item_drop_list_ers",ERS_OPT_NONE);
-
 	add_timer_func_list(pet_hungry,"pet_hungry");
 	add_timer_func_list(pet_ai_hard,"pet_ai_hard");
 	add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris]
@@ -2477,8 +2480,7 @@ void do_init_pet(void)
  */
 void do_final_pet(void)
 {
-	ers_destroy(item_drop_ers);
-	ers_destroy(item_drop_list_ers);
+	pet_delayed_drops.clear();
 
 	pet_autobonuses.clear();
 

+ 1 - 1
src/map/pet.hpp

@@ -243,7 +243,7 @@ int pet_menu(map_session_data *sd,int menunum);
 int pet_change_name(map_session_data *sd,char *name);
 int pet_change_name_ack(map_session_data *sd, char* name, int flag);
 int pet_equipitem(map_session_data *sd,int index);
-int pet_lootitem_drop(struct pet_data *pd,map_session_data *sd);
+void pet_lootitem_drop( pet_data& pd, map_session_data* sd );
 int pet_attackskill(struct pet_data *pd, int target_id);
 TIMER_FUNC(pet_skill_support_timer); // [Skotlex]
 TIMER_FUNC(pet_skill_bonus_timer); // [Valaris]

+ 206 - 106
src/map/script.cpp

@@ -932,11 +932,11 @@ static const char* skip_word(const char* p)
 static int add_word(const char* p)
 {
 	char* word;
-	int len;
 	int i;
 
 	// Check for a word
-	len = skip_word(p) - p;
+	size_t len = skip_word( p ) - p;
+
 	if( len == 0 )
 		disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alphanumeric characters, and valid variable prefixes/postfixes.", p);
 
@@ -6073,7 +6073,7 @@ BUILDIN_FUNC(percentheal)
 		return SCRIPT_CMD_SUCCESS;
 
 #ifdef RENEWAL
-	if( sd->sc.getSCE(SC_EXTREMITYFIST2) )
+	if( sd->sc.getSCE(SC_EXTREMITYFIST) )
 		sp = 0;
 #endif
 
@@ -7617,9 +7617,7 @@ BUILDIN_FUNC(getitem)
 	int get_count, i;
 	t_itemid nameid;
 	unsigned short amount;
-	struct item it;
-	map_session_data *sd;
-	unsigned char flag = 0;
+	map_session_data* sd;
 	const char* command = script_getfuncname(st);
 	std::shared_ptr<item_data> id;
 
@@ -7648,7 +7646,8 @@ BUILDIN_FUNC(getitem)
 	if( (amount = script_getnum(st,3)) <= 0)
 		return SCRIPT_CMD_SUCCESS; //return if amount <=0, skip the useles iteration
 
-	memset(&it,0,sizeof(it));
+	item it = {};
+
 	it.nameid = nameid;
 	it.identify = 1;
 	it.bound = BOUND_NONE;
@@ -7680,11 +7679,12 @@ BUILDIN_FUNC(getitem)
 		// if not pet egg
 		if (!pet_create_egg(sd, nameid))
 		{
-			if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
-			{
+			e_additem_result flag = pc_additem( sd, &it, get_count, LOG_TYPE_SCRIPT );
+
+			if( flag != ADDITEM_SUCCESS ){
 				clif_additem(sd, 0, 0, flag);
-				if( pc_candrop(sd,&it) )
-					map_addflooritem(&it,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+				ShowError( "buildin_getitem: Failed to add the item to player.\n" );
+				return SCRIPT_CMD_FAILURE;
 			}
 		}
 	}
@@ -7719,7 +7719,7 @@ BUILDIN_FUNC(getitem)
  *------------------------------------------*/
 BUILDIN_FUNC(getitem2)
 {
-	TBL_PC *sd;
+	map_session_data* sd;
 	char bound = BOUND_NONE;
 	const char* command = script_getfuncname(st);
 	int offset = 0;
@@ -7792,7 +7792,7 @@ BUILDIN_FUNC(getitem2)
 	t_itemid c3 = script_getnum(st,9);
 	t_itemid c4 = script_getnum(st,10);
 
-	struct item item_tmp = {};
+	item item_tmp = {};
 
 	if( item_data ) {
 		if( item_data->type == IT_WEAPON || item_data->type == IT_ARMOR || item_data->type == IT_SHADOWGEAR ) {
@@ -7847,12 +7847,12 @@ BUILDIN_FUNC(getitem2)
 			// if not pet egg
 			if (!pet_create_egg(sd, nameid))
 			{
-				unsigned char flag = 0;
-				if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT)))
-				{
+				e_additem_result flag = pc_additem( sd, &item_tmp, get_count, LOG_TYPE_SCRIPT );
+
+				if( flag != ADDITEM_SUCCESS ){
 					clif_additem(sd, 0, 0, flag);
-					if( pc_candrop(sd,&item_tmp) )
-						map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+					ShowError( "buildin_getitem2: Failed to add the item to player.\n" );
+					return SCRIPT_CMD_FAILURE;
 				}
 			}
 		}
@@ -10961,13 +10961,13 @@ BUILDIN_FUNC(itemskill)
 BUILDIN_FUNC(produce)
 {
 	int trigger;
-	TBL_PC* sd;
+	map_session_data* sd;
 
 	if( !script_rid2sd(sd) )
 		return SCRIPT_CMD_SUCCESS;
 
 	trigger=script_getnum(st,2);
-	clif_skill_produce_mix_list(sd, -1, trigger);
+	clif_skill_produce_mix_list( *sd, -1, trigger );
 	return SCRIPT_CMD_SUCCESS;
 }
 /*==========================================
@@ -10976,13 +10976,13 @@ BUILDIN_FUNC(produce)
 BUILDIN_FUNC(cooking)
 {
 	int trigger;
-	TBL_PC* sd;
+	map_session_data* sd;
 
 	if( !script_rid2sd(sd) )
 		return SCRIPT_CMD_SUCCESS;
 
 	trigger=script_getnum(st,2);
-	clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
+	clif_cooking_list( *sd, trigger, AM_PHARMACY, 1, 1 );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -13988,7 +13988,7 @@ BUILDIN_FUNC(getequipcardcnt)
 BUILDIN_FUNC(successremovecards) {
 	int i=-1,c,cardflag=0;
 
-	TBL_PC* sd;
+	map_session_data* sd;
 	int num;
 
 	if( !script_rid2sd(sd) )
@@ -14008,24 +14008,26 @@ BUILDIN_FUNC(successremovecards) {
 
 	for( c = sd->inventory_data[i]->slots - 1; c >= 0; --c ) {
 		if( sd->inventory.u.items_inventory[i].card[c] && itemdb_type(sd->inventory.u.items_inventory[i].card[c]) == IT_CARD ) {// extract this card from the item
-			unsigned char flag = 0;
-			struct item item_tmp;
-			memset(&item_tmp,0,sizeof(item_tmp));
+			item item_tmp = {};
+
 			cardflag = 1;
 			item_tmp.nameid   = sd->inventory.u.items_inventory[i].card[c];
 			item_tmp.identify = 1;
 
-			if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){	// get back the cart in inventory
+			e_additem_result flag = pc_additem( sd, &item_tmp, 1, LOG_TYPE_SCRIPT );
+
+			// get back the card in inventory
+			if( flag != ADDITEM_SUCCESS ){
 				clif_additem(sd,0,0,flag);
-				map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+				ShowError( "buildin_successremovecards: Failed to add the item to player.\n" );
+				return SCRIPT_CMD_FAILURE;
 			}
 		}
 	}
 
-	if(cardflag == 1) {//if card was remove remplace item with no card
-		unsigned char flag = 0;
-		struct item item_tmp;
-		memset(&item_tmp,0,sizeof(item_tmp));
+	// if card was removed, replace item with no card
+	if(cardflag == 1) {
+		item item_tmp = {};
 
 		item_tmp.nameid      = sd->inventory.u.items_inventory[i].nameid;
 		item_tmp.identify    = 1;
@@ -14045,9 +14047,14 @@ BUILDIN_FUNC(successremovecards) {
 		}
 
 		pc_delitem(sd,i,1,0,3,LOG_TYPE_SCRIPT);
-		if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){	//chk if can be spawn in inventory otherwise put on floor
+
+		e_additem_result flag = pc_additem( sd, &item_tmp, 1, LOG_TYPE_SCRIPT );
+
+		// get back the card in inventory
+		if( flag != ADDITEM_SUCCESS ){
 			clif_additem(sd,0,0,flag);
-			map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+			ShowError( "buildin_successremovecards: Failed to add the item to player.\n" );
+			return SCRIPT_CMD_FAILURE;
 		}
 
 		clif_misceffect( sd->bl, NOTIFYEFFECT_REFINE_SUCCESS );
@@ -14064,7 +14071,7 @@ BUILDIN_FUNC(successremovecards) {
 BUILDIN_FUNC(failedremovecards) {
 	int i=-1,c,cardflag=0;
 
-	TBL_PC* sd;
+	map_session_data* sd;
 	int num;
 	int typefail;
 
@@ -14088,17 +14095,17 @@ BUILDIN_FUNC(failedremovecards) {
 			cardflag = 1;
 
 			if(typefail == 2) {// add cards to inventory, clear
-				unsigned char flag = 0;
-				struct item item_tmp;
-
-				memset(&item_tmp,0,sizeof(item_tmp));
+				item item_tmp = {};
 
 				item_tmp.nameid   = sd->inventory.u.items_inventory[i].card[c];
 				item_tmp.identify = 1;
 
-				if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+				e_additem_result flag = pc_additem( sd, &item_tmp, 1, LOG_TYPE_SCRIPT );
+
+				if( flag != ADDITEM_SUCCESS ){
 					clif_additem(sd,0,0,flag);
-					map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+					ShowError( "failedremovecards: Failed to add the item to player.\n" );
+					return SCRIPT_CMD_FAILURE;
 				}
 			}
 		}
@@ -14108,10 +14115,7 @@ BUILDIN_FUNC(failedremovecards) {
 		if(typefail == 0 || typefail == 2){	// destroy the item
 			pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
 		}else if(typefail == 1){ // destroy the card
-			unsigned char flag = 0;
-			struct item item_tmp;
-
-			memset(&item_tmp,0,sizeof(item_tmp));
+			item item_tmp = {};
 
 			item_tmp.nameid      = sd->inventory.u.items_inventory[i].nameid;
 			item_tmp.identify    = 1;
@@ -14132,9 +14136,12 @@ BUILDIN_FUNC(failedremovecards) {
 
 			pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
 
-			if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+			e_additem_result flag = pc_additem( sd, &item_tmp, 1, LOG_TYPE_SCRIPT );
+
+			if( flag != ADDITEM_SUCCESS ){
 				clif_additem(sd,0,0,flag);
-				map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+				ShowError( "failedremovecards: Failed to add the item to player.\n" );
+				return SCRIPT_CMD_FAILURE;
 			}
 		}
 		clif_misceffect( sd->bl, NOTIFYEFFECT_REFINE_FAILURE );
@@ -14839,7 +14846,7 @@ BUILDIN_FUNC(petloot)
 	pd = sd->pd;
 	if (pd->loot != nullptr)
 	{	//Release whatever was there already and reallocate memory
-		pet_lootitem_drop(pd, pd->master);
+		pet_lootitem_drop( *pd, pd->master );
 		aFree(pd->loot->item);
 	}
 	else
@@ -16064,49 +16071,120 @@ BUILDIN_FUNC(chatmes)
 	return SCRIPT_CMD_SUCCESS;
 }
 
-// change npc walkspeed [Valaris]
+/**
+ * Change npc walkspeed.
+ * npcspeed <speed value> {,"<npc name>"};
+ */
 BUILDIN_FUNC(npcspeed)
 {
-	struct npc_data* nd;
-	int speed;
+	npc_data* nd;
+
+	if (script_hasdata(st, 3))
+		nd = npc_name2id(script_getstr(st, 3));
+	else
+		nd = map_id2nd(st->oid);
+
+	if (nd == nullptr) {
+		if (script_hasdata(st, 3))
+			ShowError("buildin_npcspeed: %s is a non-existing NPC.\n", script_getstr(st, 3));
+		else
+			ShowError("buildin_npcspeed: non-existing NPC.\n");
+		return SCRIPT_CMD_FAILURE;
+	}
 
-	speed = script_getnum(st,2);
-	nd =(struct npc_data *)map_id2bl(st->oid);
+	int speed = script_getnum(st, 2);
 
-	if( nd ) {
-		nd->speed = speed;
-		nd->ud.state.speed_changed = 1;
+	if (speed < MIN_WALK_SPEED || speed > MAX_WALK_SPEED) {
+		ShowError("buildin_npcspeed: invalid speed %d (min: %d, max: %d).\n", speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
+		return SCRIPT_CMD_FAILURE;
 	}
 
+	nd->speed = speed;
+
 	return SCRIPT_CMD_SUCCESS;
 }
-// make an npc walk to a position [Valaris]
+
+/**
+ * Make an npc walk to a position.
+ * npcwalkto <x>,<y> {,"<npc name>"} };
+ */
 BUILDIN_FUNC(npcwalkto)
 {
-	struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-	int x=0,y=0;
+	npc_data* nd;
 
-	x=script_getnum(st,2);
-	y=script_getnum(st,3);
+	if (script_hasdata(st, 4))
+		nd = npc_name2id(script_getstr(st, 4));
+	else
+		nd = map_id2nd(st->oid);
 
-	if(nd) {
-		if (!nd->status.hp)
-			status_calc_npc(nd, SCO_FIRST);
+	if (nd == nullptr) {
+		if (script_hasdata(st, 4))
+			ShowError("buildin_npcwalkto: %s is a non-existing NPC.\n", script_getstr(st, 4));
 		else
-			status_calc_npc(nd, SCO_NONE);
-		unit_walktoxy(&nd->bl,x,y,0);
+			ShowError("buildin_npcwalkto: non-existing NPC.\n");
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	if( nd->bl.m < 0 ){
+		ShowError( "buildin_npcwalkto: NPC is not on a map.\n" );
+		return SCRIPT_CMD_FAILURE;
 	}
+
+	struct map_data* mapdata = map_getmapdata( nd->bl.m );
+	int x = script_getnum(st, 2);
+	int y = script_getnum(st, 3);
+
+	if( x < 0 || x >= mapdata->xs || y < 0 || y >= mapdata->ys ){
+		ShowWarning( "buildin_npcwalkto: coordinates %d/%d are out of bounds in map %s(%dx%d).\n", x, y, mapdata->name, mapdata->xs, mapdata->ys );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	if (!nd->status.hp)
+		status_calc_npc(nd, SCO_FIRST);
+	else
+		status_calc_npc(nd, SCO_NONE);
+	unit_walktoxy(&nd->bl,x,y,0);
+
 	return SCRIPT_CMD_SUCCESS;
 }
 
-// stop an npc's movement [Valaris]
+/**
+ * Stop an npc's movement.
+ * npcstop {"<npc name>", {"<flag>"}};
+ */
 BUILDIN_FUNC(npcstop)
 {
-	struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+	npc_data* nd;
+
+	if (script_hasdata(st, 2))
+		nd = npc_name2id(script_getstr(st, 2));
+	else
+		nd = map_id2nd(st->oid);
+
+	if (nd == nullptr) {
+		if (script_hasdata(st, 2))
+			ShowError("buildin_npcstop: %s is a non-existing NPC.\n", script_getstr(st, 2));
+		else
+			ShowError("buildin_npcstop: non-existing NPC.\n");
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	int flag = USW_FIXPOS | USW_MOVE_FULL_CELL | USW_FORCE_STOP;
 
-	if(nd) {
-		unit_stop_walking(&nd->bl,1|4);
+	if (script_hasdata(st, 3)) {
+		flag = script_getnum(st, 3);
+
+		if (flag < USW_NONE || flag > USW_ALL) {
+			ShowError("buildin_npcstop: invalid flag %d.\n", flag);
+			return SCRIPT_CMD_FAILURE;
+		}
+
+		if (flag & USW_FORCE_STOP)
+			nd->ud.state.force_walk = false;
 	}
+
+	unit_stop_walking( &nd->bl, flag );
+
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -16870,7 +16948,7 @@ BUILDIN_FUNC(insertchar)
 {
 	const char *str = script_getstr(st,2);
 	const char *c = script_getstr(st,3);
-	int index = script_getnum(st,4);
+	size_t index = script_getnum( st, 4 );
 	char *output;
 	size_t len = strlen(str);
 
@@ -17130,7 +17208,8 @@ BUILDIN_FUNC(implode)
 //-------------------------------------------------------
 BUILDIN_FUNC(sprintf)
 {
-	unsigned int len, argc = 0, arg = 0, buf2_len = 0;
+	unsigned int argc = 0, arg = 0;
+	size_t buf2_len = 0;
 	const char* format;
 	char* p;
 	char* q;
@@ -17142,7 +17221,7 @@ BUILDIN_FUNC(sprintf)
 	// Fetch init data
 	format = script_getstr(st, 2);
 	argc = script_lastdata(st)-2;
-	len = strlen(format);
+	size_t len = strlen( format );
 
 	// Skip parsing, where no parsing is required.
 	if(len == 0) {
@@ -17271,7 +17350,7 @@ BUILDIN_FUNC(sprintf)
 // Implements C sscanf.
 //-------------------------------------------------------
 BUILDIN_FUNC(sscanf){
-	unsigned int argc, arg = 0, len;
+	unsigned int argc, arg = 0;
 	struct script_data* data;
 	map_session_data* sd = nullptr;
 	const char* str;
@@ -17288,7 +17367,7 @@ BUILDIN_FUNC(sscanf){
 	format = script_getstr(st, 3);
 	argc = script_lastdata(st)-3;
 
-	len = strlen(format);
+	size_t len = strlen(format);
 
 
 	if (len != 0 && strlen(str) == 0) {
@@ -17439,7 +17518,7 @@ BUILDIN_FUNC(replacestr)
 
 	int count = 0;
 	int numFinds = 0;
-	int i = 0, f = 0;
+	size_t i = 0, f = 0;
 
 	if(findlen == 0) {
 		ShowError("script:replacestr: Invalid search length.\n");
@@ -17520,7 +17599,6 @@ BUILDIN_FUNC(countstr)
 	bool usecase = true;
 
 	int numFinds = 0;
-	int i = 0, f = 0;
 
 	if(findlen == 0) {
 		ShowError("script:countstr: Invalid search length.\n");
@@ -17538,8 +17616,8 @@ BUILDIN_FUNC(countstr)
 		}
 	}
 
-	for(; i < inputlen; i++) {
-		for(f = 0; f <= findlen; f++) {
+	for( size_t i = 0; i < inputlen; i++ ){
+		for( size_t f = 0; f <= findlen; f++ ){
 			if(f == findlen) { //complete match
 				numFinds++;
 				i += findlen - 1;
@@ -17917,7 +17995,7 @@ BUILDIN_FUNC(callshop)
 		}
 
 		sd->npc_shopid = nd->bl.id;
-		clif_npc_market_open(sd, nd);
+		clif_npc_market_open( *sd, *nd );
 		script_pushint(st,1);
 		return SCRIPT_CMD_SUCCESS;
 	}
@@ -19186,7 +19264,12 @@ BUILDIN_FUNC(setunitdata)
 			case UMOB_X: if (!unit_walktoxy(bl, (short)value, md->bl.y, 2)) unit_movepos(bl, (short)value, md->bl.y, 0, 0); break;
 			case UMOB_Y: if (!unit_walktoxy(bl, md->bl.x, (short)value, 2)) unit_movepos(bl, md->bl.x, (short)value, 0, 0); break;
 			case UMOB_SPEED: md->base_status->speed = (unsigned short)value; status_calc_misc(bl, &md->status, md->level); calc_status = true; break;
-			case UMOB_MODE: md->base_status->mode = (enum e_mode)value; calc_status = true; break;
+			case UMOB_MODE:
+				md->base_status->mode = (enum e_mode)value;
+				// Mob mode must be updated before calling unit_refresh
+				status_calc_bl_(&md->bl, status_db.getSCB_BATTLE());
+				unit_refresh(bl);
+				break;
 			case UMOB_AI: md->special_state.ai = (enum mob_ai)value; break;
 			case UMOB_SCOPTION: md->sc.option = (unsigned short)value; break;
 			case UMOB_SEX: md->vd->sex = (char)value; unit_refresh(bl); break;
@@ -21356,8 +21439,10 @@ BUILDIN_FUNC(bg_info)
 		case BG_INFO_MAPS: {
 			size_t i;
 
-			for (i = 0; i < bg->maps.size(); i++)
-				setd_sub_str(st, nullptr, ".@bgmaps$", i, mapindex_id2name(bg->maps[i].mapindex), nullptr);
+			for( i = 0; i < bg->maps.size(); i++ ){
+				setd_sub_str( st, nullptr, ".@bgmaps$", static_cast<int>( i ), mapindex_id2name( bg->maps[i].mapindex ), nullptr );
+			}
+
 			setd_sub_num(st, nullptr, ".@bgmapscount", 0, i, nullptr);
 			script_pushint(st, i);
 			break;
@@ -22452,11 +22537,11 @@ BUILDIN_FUNC(showdigit)
  * makerune <% success bonus>{,<char_id>};
  **/
 BUILDIN_FUNC(makerune) {
-	TBL_PC* sd;
+	map_session_data* sd;
 	
 	if (!script_charid2sd(3,sd))
 		return SCRIPT_CMD_FAILURE;
-	clif_skill_produce_mix_list(sd,RK_RUNEMASTERY,24);
+	clif_skill_produce_mix_list( *sd, RK_RUNEMASTERY, 24 );
 	sd->itemid = script_getnum(st,2);
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -22817,11 +22902,10 @@ BUILDIN_FUNC(checkre)
 
 /* getrandgroupitem <group_id>{,<quantity>{,<sub_group>{,<identify>{,<char_id>}}}} */
 BUILDIN_FUNC(getrandgroupitem) {
-	TBL_PC* sd;
+	map_session_data* sd;
 	int i, get_count = 0, identify = 0;
 	uint16 group, qty = 0;
 	uint8 sub_group = 1;
-	struct item item_tmp;
 
 	if (!script_charid2sd(6, sd))
 		return SCRIPT_CMD_SUCCESS;
@@ -22838,10 +22922,14 @@ BUILDIN_FUNC(getrandgroupitem) {
 	FETCH(5, identify);
 
 	std::shared_ptr<s_item_group_entry> entry = itemdb_group.get_random_entry(group,sub_group);
-	if (!entry)
-		return SCRIPT_CMD_FAILURE; //ensure valid itemid
 
-	memset(&item_tmp,0,sizeof(item_tmp));
+	if( entry == nullptr ){
+		ShowError( "buildin_getrandgroupitem: Unable to find a random entry in group %hu for sub group %hu.\n", group, sub_group );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	item item_tmp = {};
+
 	item_tmp.nameid   = entry->nameid;
 	item_tmp.identify = identify ? 1 : itemdb_isidentified(entry->nameid);
 
@@ -22858,14 +22946,20 @@ BUILDIN_FUNC(getrandgroupitem) {
 		get_count = 1;
 	}
 
+	if( pc_inventoryblank( sd ) < get_count ){
+		ShowError( "buildin_getrandgroupitem: Not enough free space in inventory.\n" );
+		return SCRIPT_CMD_FAILURE;
+	}
+
 	for (i = 0; i < get_count; i++) {
 		// if not pet egg
 		if (!pet_create_egg(sd, entry->nameid)) {
-			unsigned char flag = 0;
-			if ((flag = pc_additem(sd,&item_tmp,item_tmp.amount,LOG_TYPE_SCRIPT))) {
+			e_additem_result flag = pc_additem( sd, &item_tmp, item_tmp.amount, LOG_TYPE_SCRIPT );
+
+			if( flag != ADDITEM_SUCCESS ){
 				clif_additem(sd,0,0,flag);
-				if (pc_candrop(sd,&item_tmp))
-					map_addflooritem(&item_tmp,item_tmp.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+				ShowError( "buildin_getrandgroupitem: Failed to add the item to player.\n" );
+				return SCRIPT_CMD_FAILURE;
 			}
 		}
 	}
@@ -22877,13 +22971,19 @@ BUILDIN_FUNC(getrandgroupitem) {
  * Gives item(s) to the attached player based on item group contents
  */
 BUILDIN_FUNC(getgroupitem) {
-	TBL_PC *sd;
+	map_session_data* sd;
 	int group_id = script_getnum(st,2);
 	
 	if (!script_charid2sd(4,sd))
-		return SCRIPT_CMD_SUCCESS;
+		return SCRIPT_CMD_FAILURE;
+
+	bool identify = false;
+
+	if( script_hasdata( st, 3 ) ){
+		identify = script_getnum( st, 3 );
+	}
 	
-	if (itemdb_group.pc_get_itemgroup(group_id, (script_hasdata(st, 3) ? script_getnum(st, 3) != 0 : false), sd)) {
+	if( itemdb_group.pc_get_itemgroup( group_id, identify, *sd ) ){
 		ShowError("buildin_getgroupitem: Invalid group id '%d' specified.\n",group_id);
 		return SCRIPT_CMD_FAILURE;
 	}
@@ -23138,7 +23238,7 @@ BUILDIN_FUNC(party_create)
 		item2 = 1;
 
 	party_create_byscript = 1;
-	script_pushint(st,party_create(sd,party_name,item1,item2));
+	script_pushint( st, party_create( *sd, party_name, item1, item2 ) );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -23190,7 +23290,7 @@ BUILDIN_FUNC(party_addmember)
 		return SCRIPT_CMD_FAILURE;
 	}
 	sd->party_invite = party_id;
-	script_pushint(st,party_add_member(party_id,sd));
+	script_pushint( st, party_add_member( party_id, *sd ) );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -23313,7 +23413,7 @@ BUILDIN_FUNC(party_destroy)
 		script_pushint(st,1);
 	}
 	else //leader leave = party broken
-		script_pushint(st,party_leave(party->data[i].sd));
+		script_pushint( st, party_leave( *party->data[i].sd ) );
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -23893,7 +23993,7 @@ BUILDIN_FUNC(clan_join){
 		return SCRIPT_CMD_FAILURE;
 	}
 
-	if( clan_member_join( sd, clan_id, sd->status.account_id, sd->status.char_id ) )
+	if( clan_member_join( *sd, clan_id, sd->status.account_id, sd->status.char_id ) )
 		script_pushint(st, true);
 	else
 		script_pushint(st, false);
@@ -23902,14 +24002,14 @@ BUILDIN_FUNC(clan_join){
 }
 
 BUILDIN_FUNC(clan_leave){
-	map_session_data *sd;
+	map_session_data* sd;
 
 	if( !script_charid2sd( 2, sd ) ){
 		script_pushint(st, false);
 		return SCRIPT_CMD_FAILURE;
 	}
 
-	if( clan_member_leave( sd, sd->status.clan_id, sd->status.account_id, sd->status.char_id ) )
+	if( clan_member_leave( *sd, sd->status.clan_id, sd->status.account_id, sd->status.char_id ) )
 		script_pushint(st, true);
 	else
 		script_pushint(st, false);
@@ -27584,9 +27684,9 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(mobcount,"ss"),
 	BUILDIN_DEF(getlook,"i?"),
 	BUILDIN_DEF(getsavepoint,"i?"),
-	BUILDIN_DEF(npcspeed,"i"), // [Valaris]
-	BUILDIN_DEF(npcwalkto,"ii"), // [Valaris]
-	BUILDIN_DEF(npcstop,""), // [Valaris]
+	BUILDIN_DEF(npcspeed,"i?"),
+	BUILDIN_DEF(npcwalkto,"ii?"),
+	BUILDIN_DEF(npcstop,"??"),
 	BUILDIN_DEF(getmapxy,"rrr??"),	//by Lorky [Lupus]
 	BUILDIN_DEF(mapid2name,"i"),
 	BUILDIN_DEF(checkoption1,"i?"),

+ 1 - 0
src/map/script.hpp

@@ -2128,6 +2128,7 @@ enum e_hat_effects : int16{
 	HAT_EF_C_BABY_GLOOM,
 	HAT_EF_WINTERNIGHTBELLS,
 	HAT_EF_NIGHTSKYOFRUTIE,
+	HAT_EF_RAINBOW_POISON_MASTER,
 	HAT_EF_MAX
 };
 

+ 13 - 2
src/map/script_constants.hpp

@@ -1922,9 +1922,9 @@
 	export_constant(SC_HIDDEN_CARD);
 	export_constant(SC_PERIOD_RECEIVEITEM_2ND);
 	export_constant(SC_PERIOD_PLUSEXP_2ND);
-	export_constant(SC_EXTREMITYFIST2);
 	export_constant(SC_POWERUP);
 	export_constant(SC_AGIUP);
+	export_constant(SC_PROTECTION);
 
 	/* status icons */
 	export_deprecated_constant2("SI_BLANK",-1);
@@ -4252,6 +4252,8 @@
 	export_constant(RC2_ILLUSION_TURTLE);
 	export_constant(RC2_RACHEL_SANCTUARY);
 	export_constant(RC2_ILLUSION_LUANDA);
+	export_constant(RC2_ILLUSION_FROZEN);
+	export_constant(RC2_ILLUSION_MOONLIGHT);
 	export_constant(RC2_MAX);
 
 	/* monster ai */
@@ -7797,6 +7799,7 @@
 	export_constant(IG_AEGIS_103034);
 	export_constant(IG_P_BOOSTER_CALL_PACKAGE);
 	export_constant(IG_P_COMPENSATION_BOX);
+	export_constant(IG_ENCHANT_STONE_BOX35);
 
 	/* unit stop walking */
 	export_constant(USW_NONE);
@@ -10499,6 +10502,7 @@
 	export_constant(HAT_EF_C_BABY_GLOOM);
 	export_constant(HAT_EF_WINTERNIGHTBELLS);
 	export_constant(HAT_EF_NIGHTSKYOFRUTIE);
+	export_constant(HAT_EF_RAINBOW_POISON_MASTER);
 
 	/* pet catch */
 	export_constant(PET_CATCH_UNIVERSAL);
@@ -10631,8 +10635,9 @@
 	export_constant(PCBLOCK_SITSTAND);
 	export_constant(PCBLOCK_COMMANDS);
 	export_constant(PCBLOCK_NPCCLICK);
-	export_constant(PCBLOCK_NPC);
 	export_constant(PCBLOCK_EMOTION);
+	export_constant(PCBLOCK_EQUIP);
+	export_constant(PCBLOCK_NPC);
 	export_constant(PCBLOCK_ALL);
 
 	/* convertpcinfo command */
@@ -10644,6 +10649,11 @@
 	export_constant(IWA_NONE);
 	export_constant(IWA_NOTDEAD);
 
+	/* npcspeed command */
+	export_constant(MIN_WALK_SPEED);
+	export_constant(MAX_WALK_SPEED);
+	export_constant(DEFAULT_NPC_WALK_SPEED);
+
 	/* skill hit */
 	export_constant(DMG_SINGLE);
 	export_constant(DMG_MULTI_HIT);
@@ -10659,6 +10669,7 @@
 	export_constant(NK_IGNOREDEFCARD);
 	export_constant(NK_IGNORELONGCARD);
 	export_constant(NK_CRITICAL);
+	export_constant(NK_SIMPLEDEFENSE);
 
 	/* skill inf */
 	export_constant(INF_PASSIVE_SKILL);

+ 3 - 3
src/map/searchstore.cpp

@@ -220,7 +220,7 @@ void searchstore_query(map_session_data* sd, unsigned char type, unsigned int mi
 
 	if( !sd->searchstore.items.empty() ) {
 		// present results
-		clif_search_store_info_ack(sd);
+		clif_search_store_info_ack( *sd );
 
 		// one page displayed
 		sd->searchstore.pages++;
@@ -229,7 +229,7 @@ void searchstore_query(map_session_data* sd, unsigned char type, unsigned int mi
 		searchstore_clear(sd);
 
 		// update uses
-		clif_search_store_info_ack(sd);
+		clif_search_store_info_ack( *sd );
 
 		// notify of failure
 		clif_search_store_info_failed(sd, SSI_FAILED_NOTHING_SEARCH_ITEM);
@@ -259,7 +259,7 @@ void searchstore_next(map_session_data* sd)
 		return;
 
 	// present results
-	clif_search_store_info_ack(sd);
+	clif_search_store_info_ack( *sd );
 
 	// one more page displayed
 	sd->searchstore.pages++;

+ 29 - 38
src/map/skill.cpp

@@ -216,7 +216,7 @@ int skill_get_weapontype( uint16 skill_id )                        { skill_get(s
 int skill_get_ammotype( uint16 skill_id )                          { skill_get(skill_id, skill_db.find(skill_id)->require.ammo); }
 int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv )         { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->require.ammo_qty); }
 int skill_get_state( uint16 skill_id )                             { skill_get(skill_id, skill_db.find(skill_id)->require.state); }
-int skill_get_status_count( uint16 skill_id )                      { skill_get(skill_id, skill_db.find(skill_id)->require.status.size()); }
+size_t skill_get_status_count( uint16 skill_id )                   { skill_get(skill_id, skill_db.find(skill_id)->require.status.size()); }
 int skill_get_spiritball( uint16 skill_id, uint16 skill_lv )       { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->require.spiritball); }
 sc_type skill_get_sc(int16 skill_id)                               { if (!skill_check(skill_id)) return SC_NONE; return skill_db.find(skill_id)->sc; }
 
@@ -2524,11 +2524,7 @@ int skill_onskillusage(map_session_data *sd, struct block_list *bl, uint16 skill
 
 /* Splitted off from skill_additional_effect, which is never called when the
  * attack skill kills the enemy. Place in this function counter status effects
- * when using skills (eg: Asura's sp regen penalty, or counter-status effects
- * from cards) that will take effect on the source, not the target. [Skotlex]
- * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
- * type of skills, so not every instance of skill_additional_effect needs a call
- * to this one.
+ * when using skills that will take effect on the source, not the target. [Skotlex]
  */
 int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, t_tick tick)
 {
@@ -2576,9 +2572,6 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
 	}
 
 	switch(skill_id) {
-	case MO_EXTREMITYFIST:
-		sc_start(src,src,SC_EXTREMITYFIST,100,skill_lv,skill_get_time2(skill_id,skill_lv));
-		break;
 	case GS_FULLBUSTER:
 		sc_start(src,src,SC_BLIND,2*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
 		break;
@@ -5517,11 +5510,9 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 			skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
 			if (skill_id == MO_EXTREMITYFIST) {
 				status_set_sp(src, 0, 0);
+				sc_start(src, src, SC_EXTREMITYFIST, 100, skill_lv, skill_get_time(skill_id, skill_lv));
 				status_change_end(src, SC_EXPLOSIONSPIRITS);
 				status_change_end(src, SC_BLADESTOP);
-#ifdef RENEWAL
-				sc_start(src,src,SC_EXTREMITYFIST2,100,skill_lv,skill_get_time(skill_id,skill_lv));
-#endif
 			} else {
 				status_set_hp(src, 1, 0);
 				status_change_end(src, SC_NEN);
@@ -5996,7 +5987,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 	case KN_BOWLINGBASH:
 		if (flag & 1) {
 			skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, (skill_area_temp[0]) > 0 ? SD_ANIMATION | skill_area_temp[0] : skill_area_temp[0]);
-			skill_blown(src, bl, skill_get_blewcount(skill_id, skill_lv), -1, BLOWN_NONE);
 		} else {
 			skill_area_temp[0] = map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
 			map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR|BL_SKILL, src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id);
@@ -7540,7 +7530,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			break;
 		}
 		else {
-			int abra_skill_id = 0, abra_skill_lv, checked = 0, checked_max = abra_db.size() * 3;
+			int abra_skill_id = 0, abra_skill_lv;
+			size_t checked = 0, checked_max = abra_db.size() * 3;
 
 			do {
 				auto abra_spell = abra_db.random();
@@ -7835,6 +7826,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case SU_TUNAPARTY:
 	case SU_GROOMING:
 	case SU_CHATTERING:
+	case ALL_RAY_OF_PROTECTION:
 		clif_skill_nodamage(bl,bl,skill_id,skill_lv,
 			sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
 		break;
@@ -8557,7 +8549,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 
 	case AM_PHARMACY:
 		if(sd) {
-			clif_skill_produce_mix_list(sd,skill_id,22);
+			clif_skill_produce_mix_list( *sd, skill_id, 22 );
 			clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
 		}
 		break;
@@ -9380,7 +9372,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			eflag = pc_additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE);
 			if(eflag) {
 				clif_additem(sd,0,0,eflag);
-				map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+				if (battle_config.skill_drop_items_full)
+					map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 			}
 		}
 		break;
@@ -9545,7 +9538,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				clif_skill_nodamage(nullptr,bl,MG_SRECOVERY,sp,1);
 			if (tsc) {
 #ifdef RENEWAL
-				if (tsc->getSCE(SC_EXTREMITYFIST2))
+				if (tsc->getSCE(SC_EXTREMITYFIST))
 					sp = 0;
 #endif
 				if (tsc->getSCE(SC_NORECOVER_STATE)) {
@@ -10167,7 +10160,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 								item_tmp.amount = skill_group->require.amount[i];
 								if( item_tmp.nameid && (flag2=pc_additem(sd,&item_tmp,item_tmp.amount,LOG_TYPE_OTHER)) ){
 									clif_additem(sd,0,0,flag2);
-									map_addflooritem(&item_tmp,item_tmp.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
+									if (battle_config.skill_drop_items_full)
+										map_addflooritem(&item_tmp,item_tmp.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 								}
 							}
 						}
@@ -10181,7 +10175,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 						if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_OTHER)) )
 						{
 							clif_additem(sd,0,0,flag);
-							map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
+							if (battle_config.skill_drop_items_full)
+								map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 						}
 					}
 				}
@@ -10296,7 +10291,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 #ifdef	RENEWAL
 			sp1 = sp1 / 2;
 			sp2 = sp2 / 2;
-			if (tsc && tsc->getSCE(SC_EXTREMITYFIST2))
+			if (tsc && tsc->getSCE(SC_EXTREMITYFIST))
 				sp1 = tstatus->sp;
 #endif
 			if (tsc && tsc->getSCE(SC_NORECOVER_STATE))
@@ -10930,7 +10925,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case GC_CREATENEWPOISON:
 		if( sd )
 		{
-			clif_skill_produce_mix_list(sd,skill_id,25);
+			clif_skill_produce_mix_list( *sd, skill_id, 25 );
 			clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
 		}
 		break;
@@ -12115,7 +12110,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			sd->skill_lv_old = skill_lv;
 			if( skill_id != GN_S_PHARMACY && skill_lv > 1 )
 				qty = 10;
-			clif_cooking_list(sd,(skill_id - GN_MIX_COOKING) + 27,skill_id,qty,skill_id==GN_MAKEBOMB?5:6);
+			clif_cooking_list( *sd, ( skill_id - GN_MIX_COOKING ) + 27, skill_id, qty, skill_id == GN_MAKEBOMB ? 5 : 6 );
 			clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
 		}
 		break;
@@ -12837,9 +12832,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			sd->skill_lv_old = skill_lv;
 
 			if (skill_id == MT_M_MACHINE)
-				clif_cooking_list(sd, 31, skill_id, 1, 7);
+				clif_cooking_list( *sd, 31, skill_id, 1, 7 );
 			else // BO_BIONIC_PHARMACY
-				clif_cooking_list(sd, 32, skill_id, 1, 8);
+				clif_cooking_list( *sd, 32, skill_id, 1, 8 );
 			clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
 		}
 		break;
@@ -13478,15 +13473,9 @@ TIMER_FUNC(skill_castend_id){
 		//Consume SP/spheres
 		skill_consume_requirement(sd,ud->skill_id, ud->skill_lv,1);
 		status_set_sp(src, 0, 0);
-		sc = &sd->sc;
-		if (sc->count)
-		{	//End states
-			status_change_end(src, SC_EXPLOSIONSPIRITS);
-			status_change_end(src, SC_BLADESTOP);
-#ifdef RENEWAL
-			sc_start(src,src, SC_EXTREMITYFIST2, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv));
-#endif
-		}
+		sc_start(src, src, SC_EXTREMITYFIST, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv));
+		status_change_end(src, SC_EXPLOSIONSPIRITS);
+		status_change_end(src, SC_BLADESTOP);
 		if( target && target->m == src->m ) { //Move character to target anyway.
 			short x, y;
 			short dir = map_calc_dir(src,target->x,target->y);
@@ -22391,7 +22380,7 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 								if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
 									clif_additem(sd,0,0,flag);
 									if( battle_config.skill_drop_items_full ){
-										map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+										map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 									}
 								}
 							}
@@ -22411,7 +22400,7 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 			if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
 				clif_additem(sd,0,0,flag);
 				if( battle_config.skill_drop_items_full ){
-					map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+					map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 				}
 			}
 			switch (skill_id) {
@@ -22498,7 +22487,7 @@ bool skill_produce_mix(map_session_data *sd, uint16 skill_id, t_itemid nameid, i
 					if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
 						clif_additem(sd,0,0,flag);
 						if( battle_config.skill_drop_items_full ){
-							map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+							map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 						}
 					}
 					clif_produceeffect(sd,7,nameid);
@@ -22581,7 +22570,8 @@ bool skill_arrow_create(map_session_data *sd, t_itemid nameid)
 		}
 		if ((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
 			clif_additem(sd,0,0,flag);
-			map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0,0);
+			if( battle_config.skill_drop_items_full )
+				map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,4,0);
 		}
 	}
 	return true;
@@ -22848,7 +22838,8 @@ int skill_elementalanalysis( map_session_data& sd, int n, uint16 skill_lv, unsig
 			unsigned char flag = pc_additem(&sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME);
 			if( flag != 0 ) {
 				clif_additem(&sd,0,0,flag);
-				map_addflooritem(&tmp_item,tmp_item.amount,sd.bl.m,sd.bl.x,sd.bl.y,0,0,0,0,0);
+				if( battle_config.skill_drop_items_full )
+					map_addflooritem(&tmp_item,tmp_item.amount,sd.bl.m,sd.bl.x,sd.bl.y,0,0,0,4,0);
 			}
 		}
 

+ 2 - 2
src/map/skill.hpp

@@ -58,6 +58,7 @@ enum e_skill_nk : uint8 {
 	NK_IGNOREDEFCARD,
 	NK_CRITICAL,
 	NK_IGNORELONGCARD,
+	NK_SIMPLEDEFENSE,
 	NK_MAX,
 };
 
@@ -535,7 +536,6 @@ int skill_get_hp( uint16 skill_id ,uint16 skill_lv );
 int skill_get_mhp( uint16 skill_id ,uint16 skill_lv );
 int skill_get_sp( uint16 skill_id ,uint16 skill_lv );
 int skill_get_ap( uint16 skill_id, uint16 skill_lv );
-int skill_get_status_count( uint16 skill_id );
 int skill_get_hp_rate( uint16 skill_id, uint16 skill_lv );
 int skill_get_sp_rate( uint16 skill_id, uint16 skill_lv );
 int skill_get_ap_rate( uint16 skill_id, uint16 skill_lv );
@@ -544,7 +544,7 @@ int skill_get_weapontype( uint16 skill_id );
 int skill_get_ammotype( uint16 skill_id );
 int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv );
 int skill_get_state(uint16 skill_id);
-int skill_get_status_count( uint16 skill_id );
+size_t skill_get_status_count( uint16 skill_id );
 int skill_get_spiritball( uint16 skill_id, uint16 skill_lv );
 unsigned short skill_dummy2skill_id(unsigned short skill_id);
 

+ 24 - 5
src/map/status.cpp

@@ -1138,6 +1138,25 @@ void StatusDatabase::removeByStatusFlag(block_list *bl, std::vector<e_status_cha
 		}
 	}
 }
+
+status_change::status_change(){
+	this->option = OPTION_NOTHING;
+	this->opt3 = OPT3_NORMAL;
+	this->opt1 = OPT1_NONE;
+	this->opt2 = OPT2_NONE;
+	this->count = 0;
+	this->lastEffect = SC_NONE;
+	this->lastEffectTimer = INVALID_TIMER;
+	this->cant = {};
+	this->comet_x = 0;
+	this->comet_y = 0;
+#ifndef RENEWAL
+	this->sg_counter = 0;
+#endif
+	std::fill( std::begin( this->data ), std::end( this->data ), nullptr );
+	this->lastStatus = { SC_NONE, nullptr };
+}
+
 /**
  * Accessor for a status_change_entry in a status_change
  */
@@ -1677,7 +1696,7 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
 				npc_event(sd, bg->die_event.c_str(), 0);
 		}
 
-		npc_script_event(sd,NPCE_DIE);
+		npc_script_event( *sd, NPCE_DIE );
 	}
 
 	return (int)(hp+sp+ap);
@@ -5601,7 +5620,7 @@ void status_calc_bl_main(struct block_list *bl, std::bitset<SCB_MAX> flag)
 		* piece of code triggers the walk-timer is set on INVALID_TIMER)
 		**/
 		if (ud)
-			ud->state.change_walk_target = ud->state.speed_changed = 1;
+			ud->state.change_walk_target = 1;
 	}
 
 	if(flag[SCB_STR]) {
@@ -9407,9 +9426,7 @@ void status_change_init(struct block_list *bl)
 {
 	status_change *sc = status_get_sc(bl);
 	nullpo_retv(sc);
-	memset(sc, 0, sizeof (status_change));
-	sc->lastEffect = SC_NONE;
-	sc->lastEffectTimer = INVALID_TIMER;
+	new (sc) status_change();
 }
 
 /*========================================== [Playtester]
@@ -12977,6 +12994,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 				case SC_BERSERK:
 				case SC_MERC_HPUP:
 				case SC_MERC_SPUP:
+				// Status needs to be updated immediately and not at the end of the damage
+				case SC_EXTREMITYFIST:
 					status_calc_bl_(bl, calc_flag, SCO_FORCE);
 					break;
 				default:

+ 7 - 4
src/map/status.hpp

@@ -756,7 +756,7 @@ enum sc_type : int16 {
 	/* Guild Aura */
 	SC_LEADERSHIP,
 	SC_GLORYWOUNDS,
-	SC_SOULCOLD, //508
+	SC_SOULCOLD,
 	SC_HAWKEYES,
 	/* ... */
 	SC_ODINS_POWER,
@@ -779,7 +779,7 @@ enum sc_type : int16 {
 	/* Max HP & SP */
 	SC_INCMHP,
 	SC_INCMSP,
-	SC_PARTYFLEE, // 531
+	SC_PARTYFLEE,
 	/**
 	* Kagerou & Oboro [malufett]
 	**/
@@ -1307,9 +1307,10 @@ enum sc_type : int16 {
 	SC_PERIOD_RECEIVEITEM_2ND,
 	SC_PERIOD_PLUSEXP_2ND,
 
-	SC_EXTREMITYFIST2,
-	SC_POWERUP,
+	//SC_EXTREMITYFIST2,
+	SC_POWERUP = 951,
 	SC_AGIUP,
+	SC_PROTECTION,
 
 	SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
 };
@@ -3291,6 +3292,8 @@ private:
 	std::pair<enum sc_type, struct status_change_entry *> lastStatus; // last-fetched status
 
 public:
+	status_change();
+
 	status_change_entry * getSCE(enum sc_type type);
 	status_change_entry * getSCE(uint32 type);
 	status_change_entry * createSCE(enum sc_type type);

+ 1 - 1
src/map/storage.cpp

@@ -714,7 +714,7 @@ enum e_guild_storage_log storage_guild_log_read( map_session_data* sd ){
 
 	enum e_guild_storage_log ret = storage_guild_log_read_sub( sd, log, MAX_GUILD_STORAGE_LOG_PACKET );
 
-	clif_guild_storage_log( sd, log, ret );
+	clif_guild_storage_log( *sd, log, ret );
 
 	return ret;
 }

+ 10 - 8
src/map/unit.cpp

@@ -140,7 +140,7 @@ int unit_walktoxy_sub(struct block_list *bl)
 		unit_refresh( bl, true );
 	}
 #endif
-	clif_move(ud);
+	clif_move( *ud );
 
 	if(ud->walkpath.path_pos>=ud->walkpath.path_len)
 		i = -1;
@@ -211,8 +211,9 @@ TIMER_FUNC(unit_teleport_timer){
 	else if(*mast_tid != tid || bl == nullptr)
 		return 0;
 	else {
-		TBL_PC *msd = unit_get_master(bl);
-		if(msd && !check_distance_bl(&msd->bl, bl, data)) {
+		map_session_data* msd = unit_get_master( bl );
+
+		if( msd != nullptr && !check_distance_bl( &msd->bl, bl, static_cast<int>( data ) ) ){
 			*mast_tid = INVALID_TIMER;
 			unit_warp(bl, msd->bl.m, msd->bl.x, msd->bl.y, CLR_TELEPORT );
 		} else // No timer needed
@@ -556,7 +557,7 @@ static TIMER_FUNC(unit_walktoxy_timer)
 					return 0;
 				}
 				// Resend walk packet for proper Self Destruction display.
-				clif_move(ud);
+				clif_move( *ud );
 			}
 			break;
 		case BL_NPC:
@@ -626,7 +627,7 @@ static TIMER_FUNC(unit_walktoxy_timer)
 		}
 		ud->walktimer = add_timer(tick+speed,unit_walktoxy_timer,id,speed);
 		if( md && DIFF_TICK(tick,md->dmgtick) < 3000 ) // Not required not damaged recently
-			clif_move(ud);
+			clif_move( *ud );
 	} else if(ud->state.running) { // Keep trying to run.
 		if (!(unit_run(bl, nullptr, SC_RUN) || unit_run(bl, sd, SC_WUGDASH)) )
 			ud->state.running = 0;
@@ -706,7 +707,8 @@ TIMER_FUNC(unit_delay_walktoxy_timer){
  * @return 1: Success 0: Fail (No valid bl or target)
  */
 TIMER_FUNC(unit_delay_walktobl_timer){
-	struct block_list *bl = map_id2bl(id), *tbl = map_id2bl(data);
+	block_list* bl = map_id2bl( id );
+	block_list* tbl = map_id2bl( static_cast<int>( data ) );
 
 	if(!bl || bl->prev == nullptr || tbl == nullptr)
 		return 0;
@@ -3165,10 +3167,10 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 			}
 
 			if(sd->party_invite > 0)
-				party_reply_invite(sd,sd->party_invite,0);
+				party_reply_invite( *sd, sd->party_invite, 0 );
 
 			if(sd->guild_invite > 0)
-				guild_reply_invite(sd,sd->guild_invite,0);
+				guild_reply_invite( *sd, sd->guild_invite, 0 );
 
 			if(sd->guild_alliance > 0)
 				guild_reply_reqalliance(sd,sd->guild_alliance_account,0);

+ 0 - 1
src/map/unit.hpp

@@ -52,7 +52,6 @@ struct unit_data {
 		unsigned step_attack : 1;
 		unsigned walk_easy : 1 ;
 		unsigned running : 1;
-		unsigned speed_changed : 1;
 		unsigned walk_script : 1;
 		unsigned blockedmove : 1;
 		unsigned blockedskill : 1;

+ 3 - 2
src/map/vending.cpp

@@ -94,7 +94,7 @@ void vending_vendinglistreq(map_session_data* sd, int id)
 
 	sd->vended_id = vsd->vender_id;  // register vending uid
 
-	clif_vendinglist( sd, vsd );
+	clif_vendinglist( *sd, *vsd );
 }
 
 /**
@@ -384,7 +384,7 @@ int8 vending_openvending( map_session_data& sd, const char* message, const uint8
 		Sql_ShowDebug(mmysql_handle);
 	StringBuf_Destroy(&buf);
 
-	clif_openvending(&sd,sd.bl.id,sd.vending);
+	clif_openvending( sd );
 	clif_showvendingboard( sd );
 
 	idb_put(vending_db, sd.status.char_id, &sd);
@@ -612,6 +612,7 @@ void do_init_vending_autotrade(void)
 
 				// initialize player
 				CREATE(at->sd, map_session_data, 1); // TODO: Dont use Memory Manager allocation anymore and rely on the C++ container
+				new (at->sd) map_session_data();
 				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
 				at->sd->state.autotrade = 1|2;
 				if (battle_config.autotrade_monsterignore)

+ 8 - 6
src/tool/csv2yaml.cpp

@@ -2331,7 +2331,7 @@ static bool quest_read_db( char *split[], size_t columns, size_t current ){
 	std::string title = split[17];
 	
 	if (columns > 18) { // If the title has a comma in it, concatenate
-		int col = 18;
+		size_t col = 18;
 
 		while (col < columns) {
 			title += ',' + std::string(split[col]);
@@ -2472,7 +2472,7 @@ static bool instance_readdb_sub( char* str[], size_t columns, size_t current ){
 		body << YAML::Key << "AdditionalMaps";
 		body << YAML::BeginMap;
 
-		for (int i = 7; i < columns; i++) {
+		for( size_t i = 7; i < columns; i++ ){
 			if (!strlen(str[i]))
 				continue;
 
@@ -3110,7 +3110,8 @@ static bool itemdb_read_randomopt_group( char* str[], size_t columns, size_t cur
 	if (group == nullptr)
 		group_entry.rate.push_back((uint16)strtoul(str[1], nullptr, 10));
 
-	for (int j = 0, k = 2; k < columns && j < MAX_ITEM_RDM_OPT; k += 3) {
+	uint16 j = 0;
+	for( size_t k = 2; k < columns && j < MAX_ITEM_RDM_OPT; k += 3 ){
 		int32 randid_tmp = -1;
 
 		for (const auto &opt : rand_opt_db) {
@@ -3876,7 +3877,7 @@ static bool skill_parse_row_createarrowdb( char* split[], size_t columns, size_t
 
 	std::map<std::string, uint32> item_created;
 	
-	for (uint16 x = 1; x+1 < columns && split[x] && split[x+1]; x += 2) {
+	for( size_t x = 1; x + 1 < columns && split[x] && split[x + 1]; x += 2 ){
 		nameid = static_cast<t_itemid>(strtoul(split[x], nullptr, 10));
 		std::string* item_name = util::umap_find(aegis_itemnames, nameid);
 
@@ -4132,7 +4133,7 @@ static bool mob_readdb_itemratio( char* str[], size_t columns, size_t current ){
 	if (columns-2 > 0) {
 		body << YAML::Key << "List";
 		body << YAML::BeginMap;
-		for (int i = 0; i < columns-2; i++) {
+		for( size_t i = 0; i < columns - 2; i++ ){
 			uint16 mob_id = static_cast<uint16>(strtoul(str[i+2], nullptr, 10));
 			std::string* mob_name = util::umap_find( aegis_mobnames, mob_id );
 
@@ -4251,8 +4252,9 @@ static bool pc_readdb_job2( char* fields[], size_t columns, size_t current ){
 	stats.resize(MAX_LEVEL);
 	std::fill(stats.begin(), stats.end(), 0); // Fill with 0 so we don't produce arbitrary stats
 
-	for (int i = 1; i < columns; i++)
+	for( size_t i = 1; i < columns; i++ ){
 		stats[i - 1] = atoi(fields[i]);
+	}
 
 	job_db2.insert({ atoi(fields[0]), stats });
 	return true;

+ 3 - 0
src/tool/csv2yaml.vcxproj

@@ -132,6 +132,7 @@
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YAML_CPP_STATIC_DEFINE;YY_USE_CONST;MINICORE;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\yaml-cpp\include\;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -152,6 +153,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YAML_CPP_STATIC_DEFINE;YY_USE_CONST;MINICORE;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\yaml-cpp\include\;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -174,6 +176,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YAML_CPP_STATIC_DEFINE;YY_USE_CONST;MINICORE;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <ConformanceMode>true</ConformanceMode>
       <LanguageStandard>stdcpp17</LanguageStandard>
       <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\yaml-cpp\include\;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>

Some files were not shown because too many files changed in this diff