Files
2026-02-02 04:50:13 +01:00

327 lines
7.3 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/core/nuvie_defs.h"
#include "ultima/nuvie/core/obj_manager.h"
#include "ultima/nuvie/core/game.h"
#include "ultima/nuvie/core/u6_objects.h"
namespace Ultima {
namespace Nuvie {
Obj::Obj() : obj_n(0), status(0), nuvie_status(0), frame_n(0), qty(0),
quality(0), parent(nullptr), container(nullptr), x(0), y(0), z(0) {
}
Obj::Obj(Obj *sobj) {
memcpy(this, sobj, sizeof(Obj));
parent = nullptr;
container = nullptr;
}
void Obj::make_container() {
if (container == nullptr)
container = new U6LList();
return;
}
Obj *Obj::get_container_obj(bool recursive) {
Obj *obj = (is_in_container() ? (Obj *)parent : nullptr);
if (recursive) {
while (obj && obj->is_in_container())
obj = (Obj *)obj->parent;
}
return obj;
}
void Obj::set_on_map(U6LList *map_list) {
parent = map_list;
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
nuvie_status |= OBJ_LOC_MAP;
return;
}
void Obj::set_in_container(Obj *container_obj) {
parent = (void *)container_obj;
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
nuvie_status |= OBJ_LOC_CONT;
return;
}
void Obj::set_invisible(bool flag) {
if (flag)
status |= OBJ_STATUS_INVISIBLE;
else if (is_invisible())
status ^= OBJ_STATUS_INVISIBLE;
return;
}
void Obj::set_temporary(bool flag) {
if (flag)
status |= OBJ_STATUS_TEMPORARY;
else if (is_temporary())
status ^= OBJ_STATUS_TEMPORARY;
return;
}
void Obj::set_ok_to_take(bool flag, bool recursive) {
if (flag)
status |= OBJ_STATUS_OK_TO_TAKE;
else if (is_ok_to_take())
status ^= OBJ_STATUS_OK_TO_TAKE;
if (recursive && container) {
for (U6Link *link = container->start(); link != nullptr; link = link->next) {
Obj *obj = (Obj *)link->data;
obj->set_ok_to_take(flag, recursive);
}
}
}
void Obj::set_in_inventory() {
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
nuvie_status |= OBJ_LOC_INV;
return;
}
void Obj::readied() { //set_readied() ??
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
nuvie_status |= OBJ_LOC_READIED;
return;
}
void Obj::set_noloc() {
parent = nullptr;
nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET; //clear location bits 0 = no loc
return;
}
void Obj::set_in_script(bool flag) {
if (flag)
nuvie_status |= NUVIE_OBJ_STATUS_SCRIPTING;
else if (is_script_obj())
nuvie_status ^= NUVIE_OBJ_STATUS_SCRIPTING;
return;
}
void Obj::set_actor_obj(bool flag) {
if (flag)
nuvie_status |= NUVIE_OBJ_STATUS_ACTOR_OBJ;
else if (is_actor_obj())
nuvie_status ^= NUVIE_OBJ_STATUS_ACTOR_OBJ;
return;
}
/* Returns true if an object is in an actor inventory, including containers and readied items. */
bool Obj::is_in_inventory(bool check_parent) const {
switch (get_engine_loc()) {
case OBJ_LOC_INV :
case OBJ_LOC_READIED :
return true;
case OBJ_LOC_CONT :
if (check_parent)
return ((Obj *)parent)->is_in_inventory(check_parent);
break;
default :
break;
}
return false;
}
uint8 Obj::get_engine_loc() const {
return (nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET);
}
Actor *Obj::get_actor_holding_obj() {
switch (get_engine_loc()) {
case OBJ_LOC_INV :
case OBJ_LOC_READIED :
return (Actor *)this->parent;
case OBJ_LOC_CONT :
return ((Obj *)parent)->get_actor_holding_obj();
default :
break;
}
return nullptr;
}
//Add child object into container, stacking if required
void Obj::add(Obj *obj, bool stack, bool addAtTail) {
if (container == nullptr)
make_container();
if (stack && Game::get_game()->get_obj_manager()->is_stackable(obj))
add_and_stack(obj, addAtTail);
else
if (!addAtTail)
container->addAtPos(0, obj);
else
container->add(obj);
obj->set_in_container(this);
return;
}
void Obj::add_and_stack(Obj *obj, bool addAtTail) {
U6Link *link;
Obj *cont_obj;
//should we recurse through nested containers?
for (link = container->start(); link != nullptr;) {
cont_obj = (Obj *)link->data;
link = link->next;
//match on obj_n, frame_n and quality.
if (obj->obj_n == cont_obj->obj_n && obj->frame_n == cont_obj->frame_n && obj->quality == cont_obj->quality) {
obj->qty += cont_obj->qty;
container->replace(cont_obj, obj); //replace cont_obj with obj in container list. should we do this to link->data directly?
delete_obj(cont_obj);
return;
}
}
if (!addAtTail)
container->addAtPos(0, obj); // add the object as we couldn't find another object to stack with.
else
container->add(obj);
return;
}
//Remove child object from container.
bool Obj::remove(Obj *obj) {
if (container == nullptr)
return false;
if (container->remove(obj) == false)
return false;
if (Game::get_game()->get_game_type() == NUVIE_GAME_SE) {
if (obj_n == OBJ_SE_JAR)
frame_n = 0; // empty jar frame
}
obj->x = 0;
obj->y = 0;
obj->z = 0;
obj->set_noloc();
return true;
}
Obj *Obj::find_in_container(uint16 objN, uint8 quality_, bool match_quality, uint8 frameN, bool match_frame_n, Obj **prev_obj) const {
U6Link *link;
Obj *obj;
if (container == nullptr)
return nullptr;
for (link = container->start(); link != nullptr; link = link->next) {
obj = (Obj *)link->data;
if (obj) {
if (obj->obj_n == objN && (match_quality == false || obj->quality == quality_) && (match_frame_n == false || obj->frame_n == frameN)) {
if (prev_obj != nullptr && obj == *prev_obj)
prev_obj = nullptr;
else {
if (prev_obj == nullptr || *prev_obj == nullptr)
return obj;
}
}
if (obj->container) {
obj = obj->find_in_container(objN, quality_, match_quality, frameN, match_frame_n, prev_obj);
if (obj)
return obj;
}
}
}
return nullptr;
}
uint32 Obj::get_total_qty(uint16 match_obj_n) {
U6Link *link;
Obj *obj;
uint16 total_qty = 0;
if (obj_n == match_obj_n) {
if (qty == 0)
total_qty += 1;
else
total_qty += qty;
}
if (container != nullptr) {
for (link = container->start(); link != nullptr; link = link->next) {
obj = (Obj *)link->data;
if (obj) {
if (obj->container)
total_qty += obj->get_total_qty(match_obj_n);
else if (obj->obj_n == match_obj_n) {
if (obj->qty == 0)
total_qty += 1;
else
total_qty += obj->qty;
}
}
}
}
return total_qty;
}
uint32 Obj::container_count_objects() const {
uint32 count = 0;
U6Link *link;
if (container != nullptr) {
for (link = container->start(); link != nullptr; link = link->next) {
++count;
}
}
return count;
}
bool Obj::is_ok_to_take() const {
return ((status & OBJ_STATUS_OK_TO_TAKE) || Game::get_game()->using_hackmove());
}
} // End of namespace Nuvie
} // End of namespace Ultima