/*************************************************************************/ /* bag.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "bag.h" #include "../data/items/item_instance.h" #include "../data/items/item_template.h" int Bag::get_allowed_item_types() const { return _allowed_item_types; } void Bag::set_allowed_item_types(const int value) { _allowed_item_types = value; } bool Bag::add_item(Ref item) { ERR_FAIL_COND_V(!item.is_valid(), false); ERR_FAIL_COND_V(!item->get_item_template().is_valid(), false); if (has_method("_add_item")) { return call("_add_item", item); } int max_stack = item->get_item_template()->get_stack_size(); if (max_stack > 1) { for (int i = 0; i < _items.size(); ++i) { Ref ii = _items.get(i); if (!ii.is_valid()) continue; if (ii->get_stack_size() < max_stack) { int fs = max_stack - ii->get_stack_size(); if (fs > item->get_stack_size()) { ii->set_stack_size(ii->get_stack_size() + item->get_stack_size()); item->set_stack_size(0); emit_signal("item_count_changed", Ref(this), ii, i); return true; } else { ii->set_stack_size(max_stack); item->set_stack_size(item->get_stack_size() - fs); emit_signal("item_count_changed", Ref(this), ii, i); } } } } for (int i = 0; i < _items.size(); ++i) { Ref ii = _items.get(i); if (!ii.is_valid()) { _items.set(i, item); emit_signal("item_added", Ref(this), item, i); if (get_valid_item_count() == _bag_size) { emit_signal("overburdened", Ref(this)); } return true; } } _items.push_back(item); emit_signal("item_added", Ref(this), item, _items.size() - 1); if (get_valid_item_count() == _bag_size) { emit_signal("overburdened", Ref(this)); } return true; } void Bag::add_item_at(int index, Ref item, bool signal) { if (has_method("_add_item_at")) { call("_add_item_at", index, item, signal); return; } if (_items.size() <= index) { _items.resize(index + 1); } _items.set(index, item); if (signal) emit_signal("item_added", Ref(this), item, index); } Ref Bag::get_item(const int index) { if (has_method("_get_item")) { return call("_get_item", index); } if (index >= _items.size()) { return Ref(); } return _items.get(index); } Ref Bag::remove_item(const int index) { if (has_method("_remove_item")) { return call("_remove_item", index); } ERR_FAIL_INDEX_V(index, _items.size(), Ref()); Ref ii = _items.get(index); if (!ii.is_valid()) return ii; _items.set(index, Ref()); emit_signal("item_removed", Ref(this), ii, index); if (get_valid_item_count() + 1 == _bag_size) { emit_signal("overburden_removed", Ref(this)); } return ii; } void Bag::swap_items(const int item1_index, const int item2_index) { if (has_method("_swap_items")) { call("_swap_items", item1_index, item2_index); return; } ERR_FAIL_INDEX(item1_index, _items.size()); ERR_FAIL_INDEX(item2_index, _items.size()); Ref ii = _items.get(item1_index); _items.set(item1_index, _items.get(item2_index)); _items.set(item2_index, ii); emit_signal("item_swapped", Ref(this), item1_index, item2_index); } void Bag::change_item_equip(int slot_id, Ref item) { if (has_method("_change_item_equip")) { call("_change_item_equip", slot_id, item); return; } ERR_FAIL_INDEX(slot_id, _items.size()); _items.set(slot_id, item); emit_signal("change_item_equip", Ref(this), slot_id, item); } void Bag::set_item_count(int slot_id, int new_count) { ERR_FAIL_INDEX(slot_id, _items.size()); Ref ii = _items.get(slot_id); ERR_FAIL_COND(!ii.is_valid()); ii->set_stack_size(new_count); emit_signal("item_count_changed", Ref(this), ii, slot_id); } bool Bag::can_add_item(const Ref item) { if (has_method("_can_add_item")) { return call("_can_add_item", item); } return true; } int Bag::get_item_count() { if (has_method("_get_item_count")) { return call("_get_item_count"); } return _items.size(); } int Bag::get_valid_item_count() { if (has_method("_get_valid_item_count")) { return call("_get_valid_item_count"); } int ii = 0; for (int i = 0; i < _items.size(); ++i) { if (_items.get(i).is_valid()) ++ii; } return ii; } int Bag::get_size() { if (has_method("_get_size")) { return call("_get_size"); } return _bag_size; } void Bag::set_size(const int size) { if (has_method("_set_size")) { call("_set_size", size); return; } int item_count = get_valid_item_count(); if (_bag_size > size && _bag_size > item_count && size <= item_count) { _bag_size = size; emit_signal("overburdened", Ref(this)); return; } _bag_size = size; if (_items.size() < _bag_size) { _items.resize(_bag_size); } emit_signal("size_changed", Ref(this)); } bool Bag::is_full() { if (has_method("_is_full")) { return call("_is_full"); } return false; } bool Bag::is_overburdened() { if (has_method("_is_overburdened")) { return call("_is_overburdened"); } return _bag_size < get_valid_item_count(); } bool Bag::has_item(Ref item, int count) { return call("_has_item", item, count); } bool Bag::_has_item(Ref item, int count) { int c = 0; for (int i = 0; i < _items.size(); ++i) { Ref ii = _items.get(i); if (ii.is_valid()) { if (ii->get_item_template() == item) { c += ii->get_stack_size(); if (c >= count) { return true; } } } } return false; } void Bag::remove_items(Ref item, int count) { call("_remove_items", item, count); } void Bag::_remove_items(Ref item, int count) { int c = count; for (int i = 0; i < _items.size(); ++i) { Ref ii = _items.get(i); if (ii.is_valid()) { if (ii->get_item_template() == item) { int ss = ii->get_stack_size(); if (ss > c) { ii->set_stack_size(ss - c); emit_signal("item_count_changed", Ref(this), ii, i); return; } else if (ss < c) { c -= ii->get_stack_size(); remove_item(i); } else if (ss == c) { remove_item(i); return; } } } } } Dictionary Bag::to_dict() { return call("_to_dict"); } void Bag::from_dict(const Dictionary &dict) { call("_from_dict", dict); } Dictionary Bag::_to_dict() { Dictionary dict; dict["allowed_item_types"] = _allowed_item_types; dict["bag_size"] = _bag_size; Dictionary items; for (int i = 0; i < _items.size(); ++i) { Ref ii = _items[i]; if (ii.is_valid()) items[i] = ii->to_dict(); } dict["items"] = items; return dict; } void Bag::_from_dict(const Dictionary &dict) { _items.clear(); set_size(dict.get("bag_size", 0)); Dictionary items = dict.get("items", Dictionary()); Array keys = items.keys(); for (int i = 0; i < keys.size(); ++i) { Ref ii; ii.instance(); ii->from_dict(items[keys.get(i)]); int key = keys.get(i); if (key >= _items.size()) { _items.resize(key + 1); } _items.set(key, ii); } _allowed_item_types = dict.get("allowed_item_types", 0xFFFFFF); } Bag::Bag() { _allowed_item_types = 0xFFFFFF; _bag_size = 0; } Bag::~Bag() { _items.clear(); } void Bag::_bind_methods() { BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "could_add"), "_add_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"))); BIND_VMETHOD(MethodInfo("_add_item_at", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), PropertyInfo(Variant::BOOL, "signal"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), "_get_item", PropertyInfo(Variant::INT, "index"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), "_remove_item", PropertyInfo(Variant::INT, "index"))); BIND_VMETHOD(MethodInfo("_swap_items", PropertyInfo(Variant::INT, "item1_index"), PropertyInfo(Variant::INT, "item2_index"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "can"), "_can_add_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "count"), "_get_item_count")); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "count"), "_get_valid_item_count")); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::INT, "size"), "_get_size")); BIND_VMETHOD(MethodInfo("_set_size", PropertyInfo(Variant::INT, "size"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "full"), "_is_full")); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "overburdened"), "_is_overburdened")); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "has"), "_has_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemTemplate"), PropertyInfo(Variant::INT, "count"))); BIND_VMETHOD(MethodInfo("_remove_items", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemTemplate"), PropertyInfo(Variant::INT, "count"))); BIND_VMETHOD(MethodInfo("_change_item_equip", PropertyInfo(Variant::INT, "slot_id"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"))); ADD_SIGNAL(MethodInfo("item_added", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), PropertyInfo(Variant::INT, "slot_id"))); ADD_SIGNAL(MethodInfo("item_removed", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), PropertyInfo(Variant::INT, "slot_id"))); ADD_SIGNAL(MethodInfo("item_swapped", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"), PropertyInfo(Variant::INT, "slot_id_1"), PropertyInfo(Variant::INT, "slot_id_2"))); ADD_SIGNAL(MethodInfo("item_count_changed", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"), PropertyInfo(Variant::INT, "slot_id"))); ADD_SIGNAL(MethodInfo("overburdened", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"))); ADD_SIGNAL(MethodInfo("overburden_removed", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"))); ADD_SIGNAL(MethodInfo("size_changed", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"))); ADD_SIGNAL(MethodInfo("change_item_equip", PropertyInfo(Variant::OBJECT, "bag", PROPERTY_HINT_RESOURCE_TYPE, "Bag"), PropertyInfo(Variant::INT, "slot_id"), PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemInstance"))); ClassDB::bind_method(D_METHOD("get_allowed_item_types"), &Bag::get_allowed_item_types); ClassDB::bind_method(D_METHOD("set_allowed_item_types", "count"), &Bag::set_allowed_item_types); ADD_PROPERTY(PropertyInfo(Variant::INT, "allowed_item_types", PROPERTY_HINT_FLAGS, ItemEnums::BINDING_STRING_ITEM_TYPE_FLAGS), "set_allowed_item_types", "get_allowed_item_types"); ClassDB::bind_method(D_METHOD("add_item", "item"), &Bag::add_item); ClassDB::bind_method(D_METHOD("add_item_at", "index", "item", "signal"), &Bag::add_item_at, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_item", "index"), &Bag::get_item); ClassDB::bind_method(D_METHOD("remove_item", "index"), &Bag::remove_item); ClassDB::bind_method(D_METHOD("swap_items", "item1_index", "item2_index"), &Bag::swap_items); ClassDB::bind_method(D_METHOD("change_item_equip", "index", "item"), &Bag::change_item_equip); ClassDB::bind_method(D_METHOD("can_add_item", "item"), &Bag::can_add_item); ClassDB::bind_method(D_METHOD("get_item_count"), &Bag::get_item_count); ClassDB::bind_method(D_METHOD("get_size"), &Bag::get_size); ClassDB::bind_method(D_METHOD("set_size", "size"), &Bag::set_size); ADD_PROPERTY(PropertyInfo(Variant::INT, "size"), "set_size", "get_size"); ClassDB::bind_method(D_METHOD("is_full"), &Bag::is_full); ClassDB::bind_method(D_METHOD("is_overburdened"), &Bag::is_overburdened); ClassDB::bind_method(D_METHOD("has_item", "item", "count"), &Bag::has_item); ClassDB::bind_method(D_METHOD("_has_item", "item", "count"), &Bag::_has_item); ClassDB::bind_method(D_METHOD("remove_items", "item", "count"), &Bag::remove_items); ClassDB::bind_method(D_METHOD("_remove_items", "item", "count"), &Bag::_remove_items); //Serialization BIND_VMETHOD(MethodInfo("_from_dict", PropertyInfo(Variant::DICTIONARY, "dict"))); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::DICTIONARY, "dict"), "_to_dict")); ClassDB::bind_method(D_METHOD("from_dict", "dict"), &Bag::from_dict); ClassDB::bind_method(D_METHOD("to_dict"), &Bag::to_dict); ClassDB::bind_method(D_METHOD("_from_dict", "dict"), &Bag::_from_dict); ClassDB::bind_method(D_METHOD("_to_dict"), &Bag::_to_dict); }