X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fitems%2Finventory.qh;h=6f6ebdc452a9f768b2626fc5aecf00e9f054876c;hb=d77d35f12d537628d021b946d5fd6aa20734042d;hp=ba824f40b41c14c470a214c33eeb0bd6e9064c4d;hpb=16e64ff58cf65d7b3e0815aadb530580a6aa251b;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/items/inventory.qh b/qcsrc/common/items/inventory.qh index ba824f40b..6f6ebdc45 100644 --- a/qcsrc/common/items/inventory.qh +++ b/qcsrc/common/items/inventory.qh @@ -2,53 +2,89 @@ #include "all.qh" +#ifdef GAMEQC CLASS(Inventory, Object) /** Stores counts of items, the id being the index */ - ATTRIBARRAY(Inventory, inv_items, int, Items_MAX); - /** Previous state */ - ATTRIB(Inventory, inventory, Inventory); + ATTRIBARRAY(Inventory, inv_items, int, REGISTRY_MAX(Items)); ENDCLASS(Inventory) /** Player inventory */ .Inventory inventory; +/** Player inventory storage (holds previous state) */ +.Inventory inventory_store; REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY) -const int Inventory_groups_major = 16; -const int Inventory_groups_minor = 8; // ceil(Items_MAX / Inventory_groups_major) +const int Inventory_groups_minor = 8; // must be a multiple of 8 (one byte) to optimize bandwidth usage +const int Inventory_groups_major = 4; // must be >= ceil(REGISTRY_COUNT(Items) / Inventory_groups_minor) +#endif +// no need to perform these checks on both server and client +#ifdef CSQC +STATIC_INIT(Inventory) +{ + if (Inventory_groups_minor / 8 != floor(Inventory_groups_minor / 8)) + error("Inventory_groups_minor is not a multiple of 8."); + int min_major_value = ceil(REGISTRY_COUNT(Items) / Inventory_groups_minor); + if (Inventory_groups_major < min_major_value) + error(sprintf("Inventory_groups_major can not be < %d.", min_major_value)); +} +#endif + +#ifdef SVQC #define G_MAJOR(id) (floor((id) / Inventory_groups_minor)) #define G_MINOR(id) ((id) % Inventory_groups_minor) +#endif #ifdef CSQC +#include + Inventory g_inventory; + +void Inventory_remove(entity this) +{ + if(g_inventory == this) + g_inventory = NULL; +} + NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew) { make_pure(this); g_inventory = this; - const int majorBits = ReadShort(); + this.entremove = Inventory_remove; + const int majorBits = Readbits(Inventory_groups_major); for (int i = 0; i < Inventory_groups_major; ++i) { if (!(majorBits & BIT(i))) { continue; } - const int minorBits = ReadByte(); + const int minorBits = Readbits(Inventory_groups_minor); for (int j = 0; j < Inventory_groups_minor; ++j) { if (!(minorBits & BIT(j))) { continue; } - const GameItem it = Items_from(Inventory_groups_minor * i + j); + const GameItem it = REGISTRY_GET(Items, Inventory_groups_minor * i + j); .int fld = inv_items[it.m_id]; int prev = this.(fld); int next = this.(fld) = ReadByte(); - LOG_TRACEF("%s: %.0f -> %.0f", it.m_name, prev, next); + + Pickup_Update(it, next - prev); + LOG_DEBUGF("%s: %.0f -> %.0f", it.m_name, prev, next); } } return true; } + +NET_HANDLE(TE_CSQC_WEAPONPICKUP, bool isnew) +{ + const Weapon it = REGISTRY_GET(Weapons, ReadByte()); + Pickup_Update(it, 1); + return true; +} #endif #ifdef SVQC -void Inventory_Write(Inventory data) +int minorBitsArr[Inventory_groups_major]; +void Inventory_Write(Inventory data, Inventory store) { if (!data) { WriteShort(MSG_ENTITY, 0); @@ -56,45 +92,38 @@ void Inventory_Write(Inventory data) } TC(Inventory, data); + for (int i = 0; i < Inventory_groups_major; ++i) + minorBitsArr[i] = 0; + int majorBits = 0; FOREACH(Items, true, { .int fld = inv_items[it.m_id]; - const bool changed = data.inventory.(fld) != data.(fld); + const bool changed = store.(fld) != data.(fld); + store.(fld) = data.(fld); if (changed) { - majorBits = BITSET(majorBits, BIT(G_MAJOR(it.m_id)), true); + int maj = G_MAJOR(it.m_id); + majorBits = BITSET(majorBits, BIT(maj), true); + minorBitsArr[maj] = BITSET(minorBitsArr[maj], BIT(G_MINOR(it.m_id)), true); } }); - WriteShort(MSG_ENTITY, majorBits); - int minorBits = 0; - int lastMaj = 0; - int maj = 0; - FOREACH(Items, majorBits & BIT(maj = G_MAJOR(it.m_id)), { - .int fld = inv_items[it.m_id]; - const bool changed = data.inventory.(fld) != (data.inventory.(fld) = data.(fld)); - if (changed) { - if (maj != lastMaj) { - lastMaj = maj; -#define X() MACRO_BEGIN \ - if (minorBits) { \ - WriteByte(MSG_ENTITY, minorBits); \ - for (int j = 0; j < Inventory_groups_minor; ++j) { \ - if (!(minorBits & BIT(j))) { \ - continue; \ - } \ - const entity it = Items_from(Inventory_groups_minor * maj + j); \ - WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \ - } \ - } \ -MACRO_END - X(); - minorBits = 0; - } - minorBits = BITSET(minorBits, BIT(G_MINOR(it.m_id)), true); - } - }); - X(); -#undef X + Writebits(MSG_ENTITY, majorBits, Inventory_groups_major); + for (int i = 0; i < Inventory_groups_major; ++i) + { + if (!(majorBits & BIT(i))) + continue; + + const int minorBits = minorBitsArr[i]; + Writebits(MSG_ENTITY, minorBits, Inventory_groups_minor); + for (int j = 0; j < Inventory_groups_minor; ++j) + { + if (!(minorBits & BIT(j))) + continue; + + const entity it = REGISTRY_GET(Items, Inventory_groups_minor * i + j); + WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); + } + } } #endif @@ -106,21 +135,38 @@ bool Inventory_Send(Inventory this, Client to, int sf) { TC(Inventory, this); WriteHeader(MSG_ENTITY, ENT_CLIENT_INVENTORY); - entity e = this.owner; - if (IS_SPEC(e)) e = PS(e.enemy); // TODO: how can this *ever* be the case? - TC(Player, e); - Inventory data = e.inventory; - Inventory_Write(data); + TC(PlayerState, this.owner); + Inventory_Write(this, to.inventory_store); return true; } -void Inventory_new(entity e) +bool Inventory_customize(entity this, entity client) { - Inventory inv = NEW(Inventory), bak = NEW(Inventory); - inv.inventory = bak; - inv.drawonlytoclient = IS_CLIENT(e) ? e : e.m_client; - Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send); + // sends to spectators too! + return (PS(client) && PS(client).inventory == this); } -void Inventory_delete(entity e) { delete(e.inventory.inventory); delete(e.inventory); } + +void Inventory_new(PlayerState this) +{ + Inventory inv = NEW(Inventory); + setcefc(inv, Inventory_customize); + this.inventory = inv; + inv.owner = this; + Net_LinkEntity(inv, false, 0, Inventory_Send); +} +void Inventory_delete(entity e) { delete(e.inventory); } void Inventory_update(entity e) { e.inventory.SendFlags = 0xFFFFFF; } + +void Inventory_clear(entity store) +{ + // NOTE: you will need to perform Inventory_update after this to update the storage entity + // (unless store is the storage entity) + FOREACH(Items, true, { + .int fld = inv_items[it.m_id]; + store.(fld) = 0; + }); +} + +void InventoryStorage_attach(entity e) { e.inventory_store = NEW(Inventory); e.inventory_store.drawonlytoclient = e; } +void InventoryStorage_delete(entity e) { delete(e.inventory_store); } #endif