summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example/clientdata/equip.xml18
-rw-r--r--example/clientdata/graphics/items/equipment/feet/boots.pngbin0 -> 333 bytes
-rw-r--r--example/clientdata/graphics/items/equipment/legs/pants.pngbin0 -> 855 bytes
-rw-r--r--example/clientdata/graphics/items/equipment/weapons/weapon-dagger-dagger.pngbin0 -> 582 bytes
-rw-r--r--example/clientdata/graphics/sprites/equipment/feet/boots-female.pngbin0 -> 8734 bytes
-rw-r--r--example/clientdata/graphics/sprites/equipment/feet/boots-female.xml8
-rw-r--r--example/clientdata/graphics/sprites/equipment/feet/boots-male.pngbin0 -> 8662 bytes
-rw-r--r--example/clientdata/graphics/sprites/equipment/feet/boots-male.xml104
-rw-r--r--example/clientdata/graphics/sprites/equipment/legs/pants-female.pngbin0 -> 19817 bytes
-rw-r--r--example/clientdata/graphics/sprites/equipment/legs/pants-female.xml104
-rw-r--r--example/clientdata/graphics/sprites/equipment/legs/pants-male.pngbin0 -> 55222 bytes
-rw-r--r--example/clientdata/graphics/sprites/equipment/legs/pants-male.xml104
-rw-r--r--example/clientdata/graphics/sprites/weapons/weapon-dagger.xml27
-rw-r--r--example/clientdata/items.xml51
-rw-r--r--example/serverdata/scripts/maps/desert.lua4
-rw-r--r--src/common/inventorydata.h2
-rw-r--r--src/common/manaserv_protocol.h21
-rw-r--r--src/game-server/inventory.cpp244
-rw-r--r--src/game-server/inventory.h7
-rw-r--r--src/game-server/item.h25
-rw-r--r--src/game-server/itemmanager.cpp37
-rw-r--r--src/game-server/state.cpp81
22 files changed, 619 insertions, 218 deletions
diff --git a/example/clientdata/equip.xml b/example/clientdata/equip.xml
index 097229c..be180a0 100644
--- a/example/clientdata/equip.xml
+++ b/example/clientdata/equip.xml
@@ -7,13 +7,13 @@
updates when making changes on it.
-->
<equip-slots>
- <slot id="1" name="hand" capacity="2" visible="true" />
- <slot id="2" name="torso" capacity="1" visible="true" />
- <slot id="3" name="arms" capacity="1" />
- <slot id="4" name="head" capacity="1" visible="true" />
- <slot id="5" name="legs" capacity="1" visible="true" />
- <slot id="6" name="ring" capacity="1" />
- <slot id="7" name="necklace" capacity="1" />
- <slot id="8" name="feet" capacity="1" />
- <slot id="9" name="ammo" capacity="1" />
+ <slot id="1" name="Hand" capacity="2" visible="true" weapon="true" />
+ <slot id="2" name="Torso" capacity="1" visible="true" />
+ <slot id="3" name="Arms" capacity="1" />
+ <slot id="4" name="Head" capacity="1" visible="true" />
+ <slot id="5" name="Legs" capacity="1" visible="true" />
+ <slot id="6" name="Ring" capacity="1" />
+ <slot id="7" name="Necklace" capacity="1" />
+ <slot id="8" name="Feet" capacity="1" visible="true" />
+ <slot id="9" name="Ammo" capacity="1" ammo="true" />
</equip-slots>
diff --git a/example/clientdata/graphics/items/equipment/feet/boots.png b/example/clientdata/graphics/items/equipment/feet/boots.png
new file mode 100644
index 0000000..9f9913f
--- /dev/null
+++ b/example/clientdata/graphics/items/equipment/feet/boots.png
Binary files differ
diff --git a/example/clientdata/graphics/items/equipment/legs/pants.png b/example/clientdata/graphics/items/equipment/legs/pants.png
new file mode 100644
index 0000000..c50daf2
--- /dev/null
+++ b/example/clientdata/graphics/items/equipment/legs/pants.png
Binary files differ
diff --git a/example/clientdata/graphics/items/equipment/weapons/weapon-dagger-dagger.png b/example/clientdata/graphics/items/equipment/weapons/weapon-dagger-dagger.png
new file mode 100644
index 0000000..cdfc370
--- /dev/null
+++ b/example/clientdata/graphics/items/equipment/weapons/weapon-dagger-dagger.png
Binary files differ
diff --git a/example/clientdata/graphics/sprites/equipment/feet/boots-female.png b/example/clientdata/graphics/sprites/equipment/feet/boots-female.png
new file mode 100644
index 0000000..ba98432
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/feet/boots-female.png
Binary files differ
diff --git a/example/clientdata/graphics/sprites/equipment/feet/boots-female.xml b/example/clientdata/graphics/sprites/equipment/feet/boots-female.xml
new file mode 100644
index 0000000..4bb2fff
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/feet/boots-female.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<sprite name="player" action="stand">
+
+ <imageset name="base" src="graphics/sprites/equipment/feet/boots-female.png|W" width="64" height="64" />
+
+ <include file="equipment/feet/boots-male.xml"/>
+
+</sprite>
diff --git a/example/clientdata/graphics/sprites/equipment/feet/boots-male.png b/example/clientdata/graphics/sprites/equipment/feet/boots-male.png
new file mode 100644
index 0000000..f441c5b
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/feet/boots-male.png
Binary files differ
diff --git a/example/clientdata/graphics/sprites/equipment/feet/boots-male.xml b/example/clientdata/graphics/sprites/equipment/feet/boots-male.xml
new file mode 100644
index 0000000..7edbcef
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/feet/boots-male.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<sprite name="player" action="stand">
+
+ <imageset name="base" src="graphics/sprites/equipment/feet/boots-male.png|W" width="64" height="64" />
+
+ <action name="stand" imageset="base">
+ <animation direction="down">
+ <frame index="0" />
+ </animation>
+ <animation direction="left">
+ <frame index="18" />
+ </animation>
+ <animation direction="up">
+ <frame index="36" />
+ </animation>
+ <animation direction="right">
+ <frame index="54" />
+ </animation>
+ </action>
+
+ <action name="walk" imageset="base">
+ <animation direction="down">
+ <sequence start="1" end="6" delay="75" />
+ </animation>
+ <animation direction="left">
+ <sequence start="19" end="24" delay="75" />
+ </animation>
+ <animation direction="up">
+ <sequence start="37" end="42" delay="75" />
+ </animation>
+ <animation direction="right">
+ <sequence start="55" end="60" delay="75" />
+ </animation>
+ </action>
+
+ <action name="sit" imageset="base">
+ <animation direction="down">
+ <frame index="7" />
+ </animation>
+ <animation direction="left">
+ <frame index="25" />
+ </animation>
+ <animation direction="up">
+ <frame index="43" />
+ </animation>
+ <animation direction="right">
+ <frame index="61" />
+ </animation>
+ </action>
+
+ <action name="dead" imageset="base">
+ <animation direction="down">
+ <frame index="8" />
+ </animation>
+ <animation direction="left">
+ <frame index="26" />
+ </animation>
+ <animation direction="up">
+ <frame index="44" />
+ </animation>
+ <animation direction="right">
+ <frame index="62" />
+ </animation>
+ </action>
+
+ <action name="attack" imageset="base">
+ <animation direction="down">
+ <sequence start="9" end="12" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="27" end="30" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="45" end="48" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="63" end="66" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+ <action name="attack_bow" imageset="base">
+ <animation direction="down">
+ <sequence start="13" end="17" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="31" end="35" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="49" end="53" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="67" end="71" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+</sprite>
diff --git a/example/clientdata/graphics/sprites/equipment/legs/pants-female.png b/example/clientdata/graphics/sprites/equipment/legs/pants-female.png
new file mode 100644
index 0000000..ad8c2e0
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/legs/pants-female.png
Binary files differ
diff --git a/example/clientdata/graphics/sprites/equipment/legs/pants-female.xml b/example/clientdata/graphics/sprites/equipment/legs/pants-female.xml
new file mode 100644
index 0000000..4f5dbcd
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/legs/pants-female.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<sprite name="player" action="stand">
+
+ <imageset name="base" src="graphics/sprites/equipment/legs/pants-female.png" width="64" height="64" />
+
+ <action name="stand" imageset="base">
+ <animation direction="down">
+ <frame index="0" />
+ </animation>
+ <animation direction="left">
+ <frame index="18" />
+ </animation>
+ <animation direction="up">
+ <frame index="36" />
+ </animation>
+ <animation direction="right">
+ <frame index="54" />
+ </animation>
+ </action>
+
+ <action name="walk" imageset="base">
+ <animation direction="down">
+ <sequence start="1" end="6" delay="75" />
+ </animation>
+ <animation direction="left">
+ <sequence start="19" end="24" delay="75" />
+ </animation>
+ <animation direction="up">
+ <sequence start="37" end="42" delay="75" />
+ </animation>
+ <animation direction="right">
+ <sequence start="55" end="60" delay="75" />
+ </animation>
+ </action>
+
+ <action name="sit" imageset="base">
+ <animation direction="down">
+ <frame index="7" />
+ </animation>
+ <animation direction="left">
+ <frame index="25" />
+ </animation>
+ <animation direction="up">
+ <frame index="43" />
+ </animation>
+ <animation direction="right">
+ <frame index="61" />
+ </animation>
+ </action>
+
+ <action name="dead" imageset="base">
+ <animation direction="down">
+ <frame index="8" />
+ </animation>
+ <animation direction="left">
+ <frame index="26" />
+ </animation>
+ <animation direction="up">
+ <frame index="44" />
+ </animation>
+ <animation direction="right">
+ <frame index="62" />
+ </animation>
+ </action>
+
+ <action name="attack" imageset="base">
+ <animation direction="down">
+ <sequence start="9" end="12" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="27" end="30" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="45" end="48" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="63" end="66" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+ <action name="attack_bow" imageset="base">
+ <animation direction="down">
+ <sequence start="13" end="17" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="31" end="35" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="49" end="53" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="67" end="71" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+</sprite>
diff --git a/example/clientdata/graphics/sprites/equipment/legs/pants-male.png b/example/clientdata/graphics/sprites/equipment/legs/pants-male.png
new file mode 100644
index 0000000..f841575
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/legs/pants-male.png
Binary files differ
diff --git a/example/clientdata/graphics/sprites/equipment/legs/pants-male.xml b/example/clientdata/graphics/sprites/equipment/legs/pants-male.xml
new file mode 100644
index 0000000..3a90c6a
--- /dev/null
+++ b/example/clientdata/graphics/sprites/equipment/legs/pants-male.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<sprite name="player" action="stand">
+
+ <imageset name="base" src="graphics/sprites/equipment/legs/pants-male.png" width="64" height="64" />
+
+ <action name="stand" imageset="base">
+ <animation direction="down">
+ <frame index="0" />
+ </animation>
+ <animation direction="left">
+ <frame index="18" />
+ </animation>
+ <animation direction="up">
+ <frame index="36" />
+ </animation>
+ <animation direction="right">
+ <frame index="54" />
+ </animation>
+ </action>
+
+ <action name="walk" imageset="base">
+ <animation direction="down">
+ <sequence start="1" end="6" delay="75" />
+ </animation>
+ <animation direction="left">
+ <sequence start="19" end="24" delay="75" />
+ </animation>
+ <animation direction="up">
+ <sequence start="37" end="42" delay="75" />
+ </animation>
+ <animation direction="right">
+ <sequence start="55" end="60" delay="75" />
+ </animation>
+ </action>
+
+ <action name="sit" imageset="base">
+ <animation direction="down">
+ <frame index="7" />
+ </animation>
+ <animation direction="left">
+ <frame index="25" />
+ </animation>
+ <animation direction="up">
+ <frame index="43" />
+ </animation>
+ <animation direction="right">
+ <frame index="61" />
+ </animation>
+ </action>
+
+ <action name="dead" imageset="base">
+ <animation direction="down">
+ <frame index="8" />
+ </animation>
+ <animation direction="left">
+ <frame index="26" />
+ </animation>
+ <animation direction="up">
+ <frame index="44" />
+ </animation>
+ <animation direction="right">
+ <frame index="62" />
+ </animation>
+ </action>
+
+ <action name="attack" imageset="base">
+ <animation direction="down">
+ <sequence start="9" end="12" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="27" end="30" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="45" end="48" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="63" end="66" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+ <action name="attack_bow" imageset="base">
+ <animation direction="down">
+ <sequence start="13" end="17" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="31" end="35" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="49" end="53" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="67" end="71" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+</sprite>
diff --git a/example/clientdata/graphics/sprites/weapons/weapon-dagger.xml b/example/clientdata/graphics/sprites/weapons/weapon-dagger.xml
new file mode 100644
index 0000000..1909aff
--- /dev/null
+++ b/example/clientdata/graphics/sprites/weapons/weapon-dagger.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<sprite name="player" action="stand">
+ <imageset name="dagger" src="graphics/sprites/weapons/weapon-sword.png" width="64" height="64" />
+
+ <action name="default" imageset="dagger">
+ </action>
+
+ <action name="attack_stab" imageset="dagger">
+ <animation direction="down">
+ <sequence start="0" end="3" delay="75" />
+ <end />
+ </animation>
+ <animation direction="left">
+ <sequence start="4" end="7" delay="75" />
+ <end />
+ </animation>
+ <animation direction="up">
+ <sequence start="8" end="11" delay="75" />
+ <end />
+ </animation>
+ <animation direction="right">
+ <sequence start="12" end="15" delay="75" />
+ <end />
+ </animation>
+ </action>
+
+</sprite> \ No newline at end of file
diff --git a/example/clientdata/items.xml b/example/clientdata/items.xml
index 6f72185..3078d9d 100644
--- a/example/clientdata/items.xml
+++ b/example/clientdata/items.xml
@@ -142,6 +142,23 @@
</effect>
<sprite>weapons/weapon-bow.xml</sprite>
</item>
+ <item id="10" max-per-slot="3" name="Dagger" attack-action="attack_stab"
+ description="A simple dagger."
+ image="equipment/weapons/weapon-dagger-dagger.png"
+ value="20">
+ <equip>
+ <slot type="hand" required="1" />
+ </equip>
+ <effect trigger="equip">
+ <autoattack basespeed="166" range="1" base="50" skill="100" >
+ <bonus attribute="0" value="1.5" />
+ </autoattack>
+ <effect trigger="existence">
+ <modifier attribute="cap1" value="-2000" />
+ </effect>
+ </effect>
+ <sprite>weapons/weapon-dagger.xml</sprite>
+ </item>
<!-- Example of equippable items -->
<item id="7" max-per-slot="1" name="Leather shirt"
@@ -160,7 +177,39 @@
<sprite gender="male">equipment/chest/chest-leather-male.xml</sprite>
<sprite gender="female">equipment/chest/chest-leather-female.xml</sprite>
</item>
-
+ <item id="11" max-per-slot="2" name="Pants"
+ description="Simple pants."
+ image="equipment/legs/pants.png"
+ value="50">
+ <equip>
+ <slot type="legs" required="1" />
+ </equip>
+ <effect trigger="equip">
+ <modifier attribute="def1" value="5" />
+ </effect>
+ <effect trigger="existence">
+ <modifier attribute="cap1" value="-250" />
+ </effect>
+ <sprite gender="male">equipment/legs/pants-male.xml</sprite>
+ <sprite gender="female">equipment/legs/pants-female.xml</sprite>
+ </item>
+ <item id="12" max-per-slot="1" name="Boots"
+ description="Simple boots."
+ image="equipment/feet/boots.png|W:#4f2d29,c89078"
+ value="40">
+ <equip>
+ <slot type="feet" required="1" />
+ </equip>
+ <effect trigger="equip">
+ <modifier attribute="def1" value="5" />
+ </effect>
+ <effect trigger="existence">
+ <modifier attribute="cap1" value="-250" />
+ </effect>
+ <sprite gender="male">equipment/feet/boots-male.xml|#4f2d29,c89078</sprite>
+ <sprite gender="female">equipment/feet/boots-female.xml|#4f2d29,c89078</sprite>
+ </item>
+
<!-- example crafting ingredients -->
<item id="8" max-per-slot="99" name="Iron"
description="Combine with one wood to create a sword"
diff --git a/example/serverdata/scripts/maps/desert.lua b/example/serverdata/scripts/maps/desert.lua
index 6d88b37..7968921 100644
--- a/example/serverdata/scripts/maps/desert.lua
+++ b/example/serverdata/scripts/maps/desert.lua
@@ -28,12 +28,12 @@ atinit(function()
create_npc("Banker", 8, 35 * TILESIZE + TILESIZE / 2, 24 * TILESIZE + TILESIZE / 2, Banker, nil)
-- A simple merchant.
- merchant_buy_table = { {"Candy", 10, 20}, {"Regenerative trinket", 10, 30}, {"Minor health potion", 10, 50} }
+ merchant_buy_table = { {"Candy", 10, 20}, {"Regenerative trinket", 10, 30}, {"Minor health potion", 10, 50}, {11, 10, 60}, {12, 10, 40} }
merchant_sell_table = { {"Candy", 10, 19}, {"Sword", 10, 30}, {"Bow", 10, 200}, {"Leather shirt", 10, 300} }
create_npc("Merchant", 3, 4 * TILESIZE + TILESIZE / 2, 16 * TILESIZE + TILESIZE / 2, npclib.talk(Merchant, merchant_buy_table, merchant_sell_table), nil)
-- Another Merchant, selling some equipment, and buying everything...
- smith_buy_table = { {"Sword", 10, 50}, {7, 10, 70} }
+ smith_buy_table = { {"Sword", 10, 50}, {7, 10, 70}, {10, 10, 20} }
create_npc("Smith", 5, 15 * TILESIZE + TILESIZE / 2, 16 * TILESIZE + TILESIZE / 2, npclib.talk(Merchant, smith_buy_table), nil)
-- The most simple NPC - Welcoming new ones around.
diff --git a/src/common/inventorydata.h b/src/common/inventorydata.h
index e1c5bfa..490abff 100644
--- a/src/common/inventorydata.h
+++ b/src/common/inventorydata.h
@@ -65,7 +65,7 @@ struct EquipmentItem
// inventory slot id -> { item }
typedef std::map< unsigned int, InventoryItem > InventoryData;
-// equip slot id -> { item id }
+// equip slot id -> { item id, item instance }
// Equipment taking up multiple equip slot ids will be referenced multiple times
typedef std::multimap< unsigned int, EquipmentItem > EquipData;
diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h
index 78cbbbf..2a340b8 100644
--- a/src/common/manaserv_protocol.h
+++ b/src/common/manaserv_protocol.h
@@ -108,12 +108,12 @@ enum {
GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, W attribute
PGMSG_RESPAWN = 0x0180, // -
GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position, B direction
- // character: S name, B hair style, B hair color, B gender, B item bitmask, { W item id }*
+ // character: S name, B hair style, B hair color, B gender, B sprite layers changed, { B slot type, W item id }*
// monster: W type id
// npc: W type id
GPMSG_BEING_LEAVE = 0x0201, // W being id
GPMSG_ITEM_APPEAR = 0x0202, // W item id, W*2 position
- GPMSG_BEING_LOOKS_CHANGE = 0x0210, // W weapon, W hat, W top clothes, W bottom clothes
+ GPMSG_BEING_LOOKS_CHANGE = 0x0210, // B sprite layers changed, { B slot type, W item id }*
PGMSG_WALK = 0x0260, // W*2 destination
PGMSG_ACTION_CHANGE = 0x0270, // B Action
GPMSG_BEING_ACTION_CHANGE = 0x0271, // W being id, B action
@@ -426,23 +426,6 @@ enum BeingDirection
RIGHT = 8
};
-/**
- * enum for sprites layers.
- * WARNING: Has to be in sync with the same enum in the Sprite class
- * of the client!
- */
-enum SpriteLayer
-{
- SPRITE_BASE = 0,
- SPRITE_SHOE,
- SPRITE_BOTTOMCLOTHES,
- SPRITE_TOPCLOTHES,
- SPRITE_HAIR,
- SPRITE_HAT,
- SPRITE_WEAPON,
- SPRITE_VECTOREND
-};
-
} // namespace ManaServ
#endif // MANASERV_PROTOCOL_H
diff --git a/src/game-server/inventory.cpp b/src/game-server/inventory.cpp
index c3d2bfc..e486f7c 100644
--- a/src/game-server/inventory.cpp
+++ b/src/game-server/inventory.cpp
@@ -600,58 +600,66 @@ bool Inventory::equip(int inventorySlot)
return false;
// Test the equip requirements. If none, it's not an equipable item.
- const ItemEquipsInfo &equipInfoList =
- itemManager->getItem(it->second.itemId)->getItemEquipData();
- if (equipInfoList.empty())
+ const ItemEquipRequirement &equipReq =
+ itemManager->getItem(it->second.itemId)->getItemEquipRequirement();
+ if (!equipReq.equipSlotId)
{
LOG_DEBUG("No equip requirements for item id: " << it->second.itemId
<< " at slot: " << inventorySlot);
return false;
}
- // Iterate through all slots requirements.
- std::list<unsigned int> equipSlotsToUnequipFirst;
- for (ItemEquipsInfo::const_iterator it2 = equipInfoList.begin(),
- it2_end = equipInfoList.end(); it2 != it2_end; ++it2)
- {
- // We first check the equipment slots for:
- // - 1. whether enough total equip slot space is available.
- // - 2. whether some other equipment is to be unequipped first.
+ // List of potential unique itemInstances to unequip first.
+ std::set<unsigned int> equipInstancesToUnequipFirst;
- // If not enough total space in the equipment slot is available,
- // we cannot equip.
- if (itemManager->getEquipSlotCapacity(it2->first) < it2->second)
- {
- LOG_DEBUG("Not enough equip capacity at slot: " << it2->first
- << ", total available: "
- << itemManager->getEquipSlotCapacity(it2->first)
- << ", required: " << it2->second);
- return false;
- }
+ // We first check the equipment slots for:
+ // - 1. whether enough total equip slot space is available.
+ // - 2. whether some other equipment is to be unequipped first.
+
+ // If not enough total space in the equipment slot is available,
+ // we cannot equip.
+ if (itemManager->getEquipSlotCapacity(equipReq.equipSlotId)
+ < equipReq.capacityRequired)
+ {
+ LOG_DEBUG("Not enough equip capacity at slot: " << equipReq.equipSlotId
+ << ", total available: "
+ << itemManager->getEquipSlotCapacity(equipReq.equipSlotId)
+ << ", required: " << equipReq.capacityRequired);
+ return false;
+ }
- // Test whether some item(s) is(are) to be unequipped first.
- if (!checkEquipmentCapacity(it2->first, it2->second))
+ // Test whether some item(s) is(are) to be unequipped first.
+ if (!checkEquipmentCapacity(equipReq.equipSlotId,
+ equipReq.capacityRequired))
+ {
+ // And test whether the unequip action would succeed first.
+ if (testUnequipScriptRequirements(equipReq.equipSlotId)
+ && hasInventoryEnoughSpace(equipReq.equipSlotId))
{
- // And test whether the unequip action would succeed first.
- if (testUnequipScriptRequirements(it2->first)
- && hasInventoryEnoughSpace(it2->first))
- {
- equipSlotsToUnequipFirst.push_back(it2->first);
- }
- else
+ // Then, we unequip each iteminstance of the equip slot
+ for (EquipData::iterator iter =
+ mPoss->equipSlots.begin();
+ iter != mPoss->equipSlots.end(); ++iter)
{
- // Some non-unequippable equipment is to be unequipped first.
- // Can be the case of cursed items,
- // or when the inventory is full, for instance.
- return false;
+ if (iter->first == equipReq.equipSlotId
+ && iter->second.itemInstance)
+ equipInstancesToUnequipFirst.insert(
+ iter->second.itemInstance);
}
}
+ else
+ {
+ // Some non-unequippable equipment is to be unequipped first.
+ // Can be the case of cursed items,
+ // or when the inventory is full, for instance.
+ return false;
+ }
}
// Potential Pre-unequipment process
- for (std::list<unsigned int>::const_iterator it3 =
- equipSlotsToUnequipFirst.begin();
- it3 != equipSlotsToUnequipFirst.end(); ++it3)
+ for (std::set<unsigned int>::const_iterator it3 =
+ equipInstancesToUnequipFirst.begin();
+ it3 != equipInstancesToUnequipFirst.end(); ++it3)
{
if (!unequip(*it3))
{
@@ -667,131 +675,135 @@ bool Inventory::equip(int inventorySlot)
//W equip slot type count, W item id, { W equip slot, W capacity used}*
MessageOut equipMsg(GPMSG_EQUIP);
equipMsg.writeInt16(it->second.itemId); // Item Id
- equipMsg.writeInt16(equipInfoList.size()); // Number of equip slot changed.
+ equipMsg.writeInt16(1); // Number of equip slot changed.
// Compute an unique equip item Instance id (unicity is per character only.)
int itemInstance = getNewEquipItemInstance();
- for (ItemEquipsInfo::const_iterator it2 = equipInfoList.begin(),
- it2_end = equipInfoList.end(); it2 != it2_end; ++it2)
+ unsigned int capacityLeft = equipReq.capacityRequired;
+ unsigned int capacityUsed = 0;
+ // Apply equipment changes
+ for (EquipData::iterator it4 = mPoss->equipSlots.begin(),
+ it4_end = mPoss->equipSlots.end(); it4 != it4_end; ++it4)
{
- unsigned int capacityLeft = it2->second;
- unsigned int capacityUsed = 0;
- // Apply equipment changes
- for (EquipData::iterator it4 = mPoss->equipSlots.begin(),
- it4_end = mPoss->equipSlots.end(); it4 != it4_end; ++it4)
- {
- if (!capacityLeft)
- break;
+ if (!capacityLeft)
+ break;
- // We've found an existing equip slot
- if (it4->first == it2->first)
+ // We've found an existing equip slot
+ if (it4->first == equipReq.equipSlotId)
+ {
+ // We've found an empty slot
+ if (it4->second.itemInstance == 0)
{
- // We've found an empty slot
- if (it4->second.itemInstance == 0)
- {
- it4->second.itemId = it->second.itemId;
- it4->second.itemInstance = itemInstance;
- --capacityLeft;
- }
- else // The slot is already in use.
- {
- ++capacityUsed;
- }
+ it4->second.itemId = it->second.itemId;
+ it4->second.itemInstance = itemInstance;
+ --capacityLeft;
+ }
+ else // The slot is already in use.
+ {
+ ++capacityUsed;
}
}
+ }
- // When there is still something to apply even when out of that loop,
- // It means that the equip multimapis missing empty slots.
- // Hence, we add them back
- if(capacityLeft)
- {
- unsigned int maxCapacity =
- itemManager->getEquipSlotCapacity(it2->first);
+ // When there is still something to apply even when out of that loop,
+ // It means that the equip multimapis missing empty slots.
+ // Hence, we add them back
+ if(capacityLeft)
+ {
+ unsigned int maxCapacity =
+ itemManager->getEquipSlotCapacity(equipReq.equipSlotId);
- // A should never happen case
- assert(maxCapacity >= capacityUsed + capacityLeft);
+ // A should never happen case
+ assert(maxCapacity >= capacityUsed + capacityLeft);
- while (capacityLeft)
- {
- EquipmentItem equipItem(it->second.itemId, itemInstance);
- mPoss->equipSlots.insert(
- std::make_pair<unsigned int, EquipmentItem>
- (it2->first, equipItem));
- --capacityLeft;
- }
+ while (capacityLeft)
+ {
+ EquipmentItem equipItem(it->second.itemId, itemInstance);
+ mPoss->equipSlots.insert(
+ std::make_pair<unsigned int, EquipmentItem>
+ (equipReq.equipSlotId, equipItem));
+ --capacityLeft;
}
-
- // Equip slot
- equipMsg.writeInt16(it2->first);
- // Capacity used
- equipMsg.writeInt16(it2->second);
- // Item instance
- equipMsg.writeInt16(itemInstance);
}
+ // Equip slot
+ equipMsg.writeInt16(equipReq.equipSlotId);
+ // Capacity used
+ equipMsg.writeInt16(equipReq.capacityRequired);
+ // Item instance
+ equipMsg.writeInt16(itemInstance);
+
// New item trigger
updateEquipmentTrigger(0, it->second.itemId);
// Remove item from inventory
removeFromSlot(inventorySlot, 1);
- if (equipMsg.getLength() > 2)
- gameHandler->sendTo(mCharacter, equipMsg);
+ gameHandler->sendTo(mCharacter, equipMsg);
+
+ // Update look when necessary
+ checkLookchanges(equipReq.equipSlotId);
return true;
}
bool Inventory::unequip(unsigned int itemInstance)
{
- // map of { itemInstance, itemId }
- std::map<unsigned int, unsigned int> itemIdListToInventory;
+ if (!itemInstance)
+ return false;
MessageOut equipMsg(GPMSG_EQUIP);
equipMsg.writeInt16(0); // Item Id, useless in case of unequip.
+ // The itemId to unequip
+ unsigned int itemId = 0;
+ unsigned int slotTypeId = 0;
+
// Empties all equip entries that point to the given equipment slot
// The equipment slots should NEVER be erased after initialization!
for (EquipData::iterator it = mPoss->equipSlots.begin(),
it_end = mPoss->equipSlots.end(); it != it_end; ++it)
{
- if (it->second.itemInstance == itemInstance)
+ if (it->second.itemInstance == itemInstance && it->second.itemId)
{
// Add the item to the inventory list if not already present there
- std::map<unsigned int, unsigned int>::const_iterator it2 =
- itemIdListToInventory.find(it->second.itemInstance);
- if (it2 == itemIdListToInventory.end())
- {
- itemIdListToInventory.insert(
- std::make_pair<unsigned int, unsigned int>
- (it->second.itemInstance, it->second.itemId));
- }
-
+ itemId = it->second.itemId;
it->second.itemId = 0;
it->second.itemInstance = 0;
+
+ // We keep track of the slot type to be able to raise a potential
+ // change in the character sprite
+ slotTypeId = it->first;
}
}
+ // When there were no corresponding item id, it means no item was to
+ // be unequipped.
+ if (!itemId)
+ return false;
+
// Number of slot types touched,
- equipMsg.writeInt16(itemIdListToInventory.size());
+ equipMsg.writeInt16(1);
- // Apply unequip trigger(s), and move the item(s) back to inventory.
- for (std::map<unsigned int, unsigned int>::const_iterator it2 =
- itemIdListToInventory.begin(), it2_end = itemIdListToInventory.end();
- it2 != it2_end; ++it2)
- {
- updateEquipmentTrigger(it2->second, 0);
- insert(it2->second, 1);
+ // Move the item back to inventory.
+ insert(itemId, 1);
- equipMsg.writeInt16(it2->first);
- equipMsg.writeInt16(0); // Capacity used, set to 0 to unequip.
- }
+ equipMsg.writeInt16(itemInstance);
+ equipMsg.writeInt16(0); // Capacity used, set to 0 to unequip.
- if (equipMsg.getLength() > 2)
- {
- gameHandler->sendTo(mCharacter, equipMsg);
- return true;
- }
+ gameHandler->sendTo(mCharacter, equipMsg);
+
+ // Apply unequip trigger
+ updateEquipmentTrigger(itemId, 0);
- return false;
+ checkLookchanges(slotTypeId);
+
+ return true;
+}
+
+void Inventory::checkLookchanges(unsigned int slotTypeId)
+{
+ if (itemManager->isEquipSlotVisible(slotTypeId))
+ mCharacter->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE);
}
diff --git a/src/game-server/inventory.h b/src/game-server/inventory.h
index 39f8321..547abdf 100644
--- a/src/game-server/inventory.h
+++ b/src/game-server/inventory.h
@@ -120,7 +120,7 @@ class Inventory
* @todo
*/
bool hasInventoryEnoughSpace(unsigned int equipmentSlot)
- { return false; }
+ { return true; }
/**
* Test the items unequipment requirements.
@@ -153,6 +153,11 @@ class Inventory
void checkInventorySize();
/**
+ * Check potential visible character sprite changes.
+ */
+ void checkLookchanges(unsigned int slotTypeId);
+
+ /**
* Apply equipment triggers.
*/
void updateEquipmentTrigger(unsigned int oldId, unsigned int itemId);
diff --git a/src/game-server/item.h b/src/game-server/item.h
index caef386..f7c380f 100644
--- a/src/game-server/item.h
+++ b/src/game-server/item.h
@@ -28,10 +28,15 @@
class Being;
class Script;
-// A pair indicating: Equipment slot id -> how much slots required.
-typedef std::pair< unsigned int, unsigned int> ItemEquipInfo;
-// The list of required slots to equip.
-typedef std::list< ItemEquipInfo > ItemEquipsInfo;
+// Indicates the equip slot "cost" to equip an item.
+struct ItemEquipRequirement {
+ ItemEquipRequirement():
+ equipSlotId(0),
+ capacityRequired(0)
+ {}
+
+ unsigned int equipSlotId, capacityRequired;
+};
/**
* State effects to beings, and actors.
@@ -226,9 +231,10 @@ class ItemClass
{ return mSpriteID; }
/**
- * Returns equip requirements.
+ * Returns equip requirement.
*/
- const ItemEquipsInfo &getItemEquipData() const { return mEquip; }
+ const ItemEquipRequirement &getItemEquipRequirement() const
+ { return mEquipReq; }
private:
/**
@@ -274,12 +280,9 @@ class ItemClass
std::multimap< ItemTriggerType, ItemEffectInfo * > mDispells;
/**
- * List of list of requirements for equipping. Only one inner list
- * need be satisfied to sucessfully equip. Checks occur in order
- * from outer front to back.
- * All conditions in an inner list must be met for success.
+ * Requirement for equipping.
*/
- ItemEquipsInfo mEquip;
+ ItemEquipRequirement mEquipReq;
friend class ItemManager;
};
diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp
index 074b834..6840368 100644
--- a/src/game-server/itemmanager.cpp
+++ b/src/game-server/itemmanager.cpp
@@ -132,16 +132,18 @@ void ItemManager::readEquipSlotsFile()
continue;
}
- bool visible = XML::getBoolProperty(node, "visible", false);
- if (visible)
+ if (slotId > 255)
{
- if (++mVisibleEquipSlotCount > 7)
- {
- LOG_WARN("Item Manager: More than 7 visible equip slot!"
- "This will not work with current netcode!");
- }
+ LOG_WARN("Item Manager: equip slot " << slotId
+ << ": (" << name << ") is superior to 255 "
+ "and has been ignored.");
+ continue;
}
+ bool visible = XML::getBoolProperty(node, "visible", false);
+ if (visible)
+ ++mVisibleEquipSlotCount;
+
EquipSlotsInfo::iterator i = mEquipSlotsInfo.find(slotId);
if (i != mEquipSlotsInfo.end())
@@ -264,6 +266,13 @@ void ItemManager::readEquipNode(xmlNodePtr equipNode, ItemClass *item)
{
if (xmlStrEqual(subNode->name, BAD_CAST "slot"))
{
+ if (item->mEquipReq.equipSlotId)
+ {
+ LOG_WARN("Item Manager: duplicate equip slot definitions!"
+ " Only the first will apply.");
+ break;
+ }
+
std::string slot = XML::getProperty(subNode, "type", std::string());
if (slot.empty())
{
@@ -273,21 +282,23 @@ void ItemManager::readEquipNode(xmlNodePtr equipNode, ItemClass *item)
if (utils::isNumeric(slot))
{
// When the slot id is given
- item->mEquip.push_back(std::make_pair(utils::stringToInt(slot),
- XML::getProperty(subNode, "required", 1)));
+ item->mEquipReq.equipSlotId = utils::stringToInt(slot);
}
else
{
// When its name is given
- item->mEquip.push_back(std::make_pair(getEquipSlotIdFromName(slot),
- XML::getProperty(subNode, "required", 1)));
+ item->mEquipReq.equipSlotId = getEquipSlotIdFromName(slot);
}
+ item->mEquipReq.capacityRequired =
+ XML::getProperty(subNode, "required", 1);
}
}
- if (item->mEquip.empty())
+
+ if (!item->mEquipReq.equipSlotId)
{
LOG_WARN("Item Manager: empty equip requirement "
- "definition for item " << item->getDatabaseID() << "!");
+ "definition for item " << item->getDatabaseID() << "!"
+ " The item will be unequippable.");
return;
}
}
diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp
index d8f9b18..30b57ca 100644
--- a/src/game-server/state.cpp
+++ b/src/game-server/state.cpp
@@ -106,59 +106,50 @@ static void updateMap(MapComposite *map)
/**
* Sets message fields describing character look.
*/
-static void serializeLooks(Character *ch, MessageOut &msg, bool full)
+static void serializeLooks(Character *ch, MessageOut &msg)
{
const EquipData &equipData = ch->getPossessions().getEquipment();
- unsigned int nb_slots = itemManager->getVisibleEquipSlotCount();
- // Bitmask describing the changed entries.
- int changed = (1 << nb_slots) - 1;
- if (!full)
- {
- // TODO: do not assume the whole equipment changed,
- // when an update is asked for.
- changed = (1 << nb_slots) - 1;
- }
+ // We'll use a set to check whether we already sent the update for the given
+ // item instance.
+ std::set<unsigned int> itemInstances;
+
+ // The map storing the info about the look changes to send
+ //{ slot type id, item id }
+ std::map <unsigned int, unsigned int> lookChanges;
- std::vector<unsigned int> items;
- items.resize(nb_slots, 0);
- // Partially build both kinds of packet, to get their sizes.
- unsigned int mask_full = 0, mask_diff = 0;
- unsigned int nb_full = 0, nb_diff = 0;
- std::map<unsigned int, EquipmentItem>::const_iterator it =
- equipData.begin();
- for (unsigned int i = 0; i < nb_slots; ++i)
+ // Note that we can send several updates on the same slot type as different
+ // items may have been equipped.
+ for (EquipData::const_iterator it = equipData.begin(),
+ it_end = equipData.end(); it != it_end; ++it)
{
- if (changed & (1 << i))
- {
- // Skip slots that have not changed, when sending an update.
- ++nb_diff;
- mask_diff |= 1 << i;
- }
- if (it == equipData.end() || it->first > i) continue;
- ItemClass *eq;
- items[i] = it->first && (eq = itemManager->getItem(it->first)) ?
- eq->getSpriteID() : 0;
- if (items[i])
+ if (!itemManager->isEquipSlotVisible(it->first))
+ continue;
+
+ if (!it->second.itemInstance
+ || itemInstances.insert(it->second.itemInstance).second)
{
- /* If we are sending the whole equipment, only filled slots have to
- be accounted for, as the other ones will be automatically cleared. */
- ++nb_full;
- mask_full |= 1 << i;
+ // When the insertion succeeds, its the first time
+ // we encounter the item, so we can send the look change.
+ // We also send empty slots for unequipment handling.
+ lookChanges.insert(
+ std::make_pair<unsigned int, unsigned int>(it->first,
+ it->second.itemId));
}
}
- // Choose the smaller payload.
- if (nb_full <= nb_diff) full = true;
-
- /* Bitmask enumerating the sent slots.
- Setting the upper bit tells the client to clear the slots beforehand. */
- int mask = full ? mask_full | (1 << 7) : mask_diff;
-
- msg.writeInt8(mask);
- for (unsigned int i = 0; i < nb_slots; ++i)
+ if (lookChanges.size() > 0)
{
- if (mask & (1 << i)) msg.writeInt16(items[i]);
+ // Number of look changes to send
+ msg.writeInt8(lookChanges.size());
+
+ for (std::map<unsigned int, unsigned int>::const_iterator it2 =
+ lookChanges.begin(), it2_end = lookChanges.end();
+ it2 != it2_end; ++it2)
+ {
+ msg.writeInt8(it2->first);
+ msg.writeInt16(it2->second);
+ }
}
}
@@ -222,7 +213,7 @@ static void informPlayer(MapComposite *map, Character *p, int worldTime)
MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE);
LooksMsg.writeInt16(oid);
Character * c = static_cast<Character * >(o);
- serializeLooks(c, LooksMsg, false);
+ serializeLooks(c, LooksMsg);
LooksMsg.writeInt16(c->getHairStyle());
LooksMsg.writeInt16(c->getHairColor());
LooksMsg.writeInt16(c->getGender());
@@ -286,7 +277,7 @@ static void informPlayer(MapComposite *map, Character *p, int worldTime)
enterMsg.writeInt8(q->getHairStyle());
enterMsg.writeInt8(q->getHairColor());
enterMsg.writeInt8(q->getGender());
- serializeLooks(q, enterMsg, true);
+ serializeLooks(q, enterMsg);
} break;
case OBJECT_MONSTER: