aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-06-11 01:56:41 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-06-11 01:58:18 -0300
commitb167d45f88cfb824a96c3a5a120c34c30768ecda (patch)
tree79d52a61067ddaf68e5097ff546f2e55d5c7023e
parent81a5d53bc566fe2c678577cb3f3e5cadd0711753 (diff)
downloadgame-b167d45f88cfb824a96c3a5a120c34c30768ecda.tar.gz
game-b167d45f88cfb824a96c3a5a120c34c30768ecda.zip
first look at `operate.cc`
-rw-r--r--TODO.md4
-rw-r--r--reference/game.c455
-rw-r--r--src/cract.cc3
-rw-r--r--src/operate.cc272
4 files changed, 276 insertions, 458 deletions
diff --git a/TODO.md b/TODO.md
index 32dcda7..2d28ca4 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,8 +1,8 @@
## TODO NEXT
-- INFO.CC
- OPERATE.CC
- HOUSES.CC
-- CRPLAYER.CC, CRNONPL.CC
+- CRPLAYER.CC
+- CRNONPL.CC
- MOVEUSE.CC
- SENDING.CC
- RECEIVING.CC
diff --git a/reference/game.c b/reference/game.c
index c00ab3e..213e714 100644
--- a/reference/game.c
+++ b/reference/game.c
@@ -20717,461 +20717,6 @@ LAB_080926bf:
__cxa_throw(puVar3,&RESULT::typeinfo,0);
}
-
-
-void Move(ulong CreatureID,Object *Obj,Object *Con,int Count,bool NoMerge,Object *Ignore)
-
-{
- Object OVar1;
- bool bVar2;
- TCreature *cr;
- ulong uVar3;
- TCreature *pTVar4;
- int iVar5;
- undefined4 *puVar6;
- TCreature *cr_1;
- char *Text;
- Object *pOVar7;
- Object *Destination;
- ulong oldCreature;
- Object *local_94;
- int x;
- int y;
- int z;
- int x_1;
- int y_1;
- int z_1;
- Object Rest;
- Object local_6c;
- Object oldCon;
- Object local_5c [4];
- Object local_4c [4];
- Object local_3c;
- Object Top;
- Object local_2c [7];
-
- // try { // try from 0809276c to 08092fab has its CatchHandler @ 08093120
- bVar2 = Object::exists(Obj);
- if (bVar2) {
- bVar2 = Object::exists(Con);
- if (!bVar2) {
- error("Move: Zielobjekt existiert nicht.\n");
- local_2c[0] = (Object)Obj->ObjectID;
- GetObjectCoordinates(local_2c,&z,&x_1,&y_1);
- Object::getObjectType(local_2c);
- error("# Objekt %d an [%d,%d,%d]\n",local_2c[0].ObjectID,z,x_1,y_1);
- goto LAB_08093028;
- }
- Object::getObjectType(local_2c);
- bVar2 = ObjectType::getFlag((ObjectType *)local_2c,CONTAINER);
- if (bVar2) {
- Object::getObjectType(local_2c);
- pOVar7 = (Object *)0x6;
- bVar2 = ObjectType::getFlag((ObjectType *)local_2c,CUMULATIVE);
- if (!bVar2) {
-LAB_08092800:
- OVar1.ObjectID = local_3c.ObjectID;
- if ((!NoMerge) &&
- (pOVar7 = Con, Object::getObjectType(local_2c),
- OVar1.ObjectID = local_3c.ObjectID, local_2c[0].ObjectID == 0)) {
- Object::getObjectType(local_2c);
- pOVar7 = (Object *)0x6;
- bVar2 = ObjectType::getFlag((ObjectType *)local_2c,CUMULATIVE);
- OVar1.ObjectID = local_3c.ObjectID;
- if (bVar2) {
- pOVar7 = Obj;
- Object::getContainer(local_2c);
- local_3c = (Object)Con->ObjectID;
- OVar1.ObjectID = local_2c[0].ObjectID;
- if (local_3c.ObjectID != local_2c[0].ObjectID) {
- GetObjectCoordinates(&local_3c,(int *)&local_94,&x,&y);
- GetTopObject((int)&local_3c,(int)local_94,x,(bool)(undefined1)y);
- local_5c[0] = (Object)NONE.ObjectID;
- local_4c[0].ObjectID = NONE.ObjectID;
- pOVar7 = local_94;
- OVar1.ObjectID = local_3c.ObjectID;
- if (local_3c.ObjectID != NONE.ObjectID) {
- local_5c[0] = (Object)Ignore->ObjectID;
- local_6c.ObjectID = local_3c.ObjectID;
- z_1 = Obj->ObjectID;
- // try { // try from 08092ff7 to 08092ffb has its CatchHandler @ 080930df
- Merge(CreatureID,(Object *)&z_1,&local_6c,Count,local_5c);
- return;
- }
- }
- }
- }
- local_3c.ObjectID = OVar1.ObjectID;
- bVar2 = Object::exists(Obj);
- if (!bVar2) {
- error(&DAT_080ff6c0,pOVar7);
- }
- Object::getObjectType((Object *)&z_1);
- bVar2 = ObjectType::getFlag((ObjectType *)&z_1,CUMULATIVE);
- if ((bVar2) && (Count == -1)) {
- Count = Object::getAttribute(Obj,AMOUNT);
- }
- z_1 = Ignore->ObjectID;
- local_6c = (Object)Obj->ObjectID;
- CheckTopMoveObject(CreatureID,&local_6c,(Object *)&z_1);
- Object::getObjectType((Object *)&z_1);
- bVar2 = z_1 != 0;
- z_1 = Obj->ObjectID;
- CheckMoveObject(CreatureID,(Object *)&z_1,bVar2);
- Object::getObjectType((Object *)&z_1);
- if (z_1 == 0) {
- z_1 = Con->ObjectID;
- local_6c = (Object)Obj->ObjectID;
- CheckMapDestination(CreatureID,&local_6c,(Object *)&z_1);
- }
- else {
- oldCreature._3_1_ = false;
- Object::getObjectType((Object *)&z_1);
- bVar2 = ObjectType::getFlag((ObjectType *)&z_1,CUMULATIVE);
- if ((bVar2) && (uVar3 = Object::getAttribute(Obj,AMOUNT), Count < (int)uVar3)) {
- oldCreature._3_1_ = true;
- }
- Object::getObjectType((Object *)&z_1);
- bVar2 = false;
- if ((0 < z_1) && (z_1 < 0xb)) {
- bVar2 = true;
- }
- if (bVar2) {
- z_1 = Con->ObjectID;
- local_6c = (Object)Obj->ObjectID;
- CheckInventoryDestination(&local_6c,(Object *)&z_1,oldCreature._3_1_);
- }
- else {
- z_1 = Con->ObjectID;
- local_6c = (Object)Obj->ObjectID;
- CheckContainerDestination(&local_6c,(Object *)&z_1);
- }
- if (oldCreature._3_1_ == false) {
- z_1 = Obj->ObjectID;
- iVar5 = CountObjects((Object *)&z_1);
- local_6c = (Object)Con->ObjectID;
- pOVar7 = local_5c;
- local_5c[0] = (Object)Obj->ObjectID;
- Destination = &local_6c;
- }
- else {
- z_1 = Con->ObjectID;
- iVar5 = 1;
- local_6c.ObjectID = NONE.ObjectID;
- pOVar7 = &local_6c;
- Destination = (Object *)&z_1;
- }
- CheckDepotSpace(CreatureID,pOVar7,Destination,iVar5);
- z_1 = Con->ObjectID;
- uVar3 = GetObjectCreatureID((Object *)&z_1);
- if (uVar3 != 0) {
- z_1 = Obj->ObjectID;
- CheckWeight(uVar3,(Object *)&z_1,Count);
- }
- }
- z_1 = NONE.ObjectID;
- Object::getObjectType(&local_6c);
- bVar2 = ObjectType::getFlag((ObjectType *)&local_6c,CUMULATIVE);
- if ((bVar2) && (uVar3 = Object::getAttribute(Obj,AMOUNT), Count < (int)uVar3)) {
- z_1 = Obj->ObjectID;
- local_6c.ObjectID = z_1;
- local_5c[0].ObjectID = z_1;
- local_4c[0].ObjectID = z_1;
- SplitObject(local_5c,(int)local_4c);
- local_6c.ObjectID = local_5c[0].ObjectID;
- Obj->ObjectID = (ulong)local_5c[0];
- }
- Object::getContainer(&local_6c);
- local_5c[0] = (Object)Obj->ObjectID;
- pOVar7 = (Object *)GetObjectCreatureID(local_5c);
- local_5c[0].ObjectID = local_6c.ObjectID;
- local_4c[0].ObjectID = local_6c.ObjectID;
- if (Con->ObjectID != local_6c.ObjectID) {
- local_3c = (Object)Obj->ObjectID;
- SeparationEvent(&local_3c,local_4c);
- }
- Object::getObjectType(local_5c);
- if ((local_5c[0].ObjectID != 99) &&
- (local_5c[0].ObjectID = NONE.ObjectID, z_1 == NONE.ObjectID)) {
- local_4c[0] = (Object)Obj->ObjectID;
- AnnounceChangedObject(local_4c,0);
- local_4c[0] = (Object)Obj->ObjectID;
- NotifyTrades(local_4c);
- local_4c[0] = (Object)Obj->ObjectID;
- iVar5 = CountObjects(local_4c);
- local_3c = (Object)Obj->ObjectID;
- NotifyDepot(CreatureID,&local_3c,iVar5);
- }
- Object::getObjectType(local_5c);
- bVar2 = ObjectType::getFlag((ObjectType *)local_5c,CONTAINER);
- if ((bVar2) && (CreatureID == 0)) {
- local_5c[0] = (Object)Obj->ObjectID;
- CloseContainer(local_5c,true);
- }
- Object::getObjectType(local_5c);
- if (local_5c[0].ObjectID == 99) {
- local_5c[0] = (Object)Obj->ObjectID;
- pTVar4 = GetCreature(local_5c);
- if (pTVar4 == (TCreature *)0x0) {
- uVar3 = Object::getCreatureID(Obj);
- error(&DAT_080ff680,uVar3);
- }
- else {
- local_5c[0] = (Object)Con->ObjectID;
- TCreature::NotifyTurn(pTVar4,local_5c);
- }
- local_5c[0] = (Object)Con->ObjectID;
- uVar3 = Object::getCreatureID(Obj);
- AnnounceMovingCreature(uVar3,local_5c);
- }
- local_5c[0] = (Object)Con->ObjectID;
- local_4c[0] = (Object)Obj->ObjectID;
- MoveObject(local_4c,local_5c);
- Object::getObjectType(local_5c);
- if (local_5c[0].ObjectID != 99) {
- local_5c[0] = (Object)Obj->ObjectID;
- AnnounceChangedObject(local_5c,1);
- local_5c[0] = (Object)Obj->ObjectID;
- NotifyTrades(local_5c);
- local_5c[0] = (Object)Obj->ObjectID;
- iVar5 = CountObjects(local_5c);
- local_4c[0] = (Object)Obj->ObjectID;
- NotifyDepot(CreatureID,local_4c,-iVar5);
- }
- local_5c[0].ObjectID = NONE.ObjectID;
- local_4c[0].ObjectID = NONE.ObjectID;
- if (z_1 != NONE.ObjectID) {
- local_4c[0].ObjectID = z_1;
- AnnounceChangedObject(local_4c,2);
- local_4c[0].ObjectID = z_1;
- NotifyTrades(local_4c);
- }
- Object::getObjectType(local_5c);
- if (local_5c[0].ObjectID == 99) {
- local_5c[0] = (Object)Obj->ObjectID;
- pTVar4 = GetCreature(local_5c);
- if (pTVar4 == (TCreature *)0x0) {
- uVar3 = Object::getCreatureID(Obj);
- error(&DAT_080ff680,uVar3);
- }
- else {
- TCreature::NotifyGo(pTVar4);
- }
- }
- Object::getObjectType(local_5c);
- local_5c[0] = (Object)Obj->ObjectID;
- NotifyCreature(local_5c[0].ObjectID,pOVar7,SUB41(local_5c,0));
- Object::getContainer(local_5c);
- Object::getObjectType(local_4c);
- local_4c[0] = (Object)Obj->ObjectID;
- local_3c.ObjectID = local_4c[0].ObjectID;
- uVar3 = GetObjectCreatureID(&local_3c);
- NotifyCreature(uVar3,(Object *)uVar3,SUB41(local_4c,0));
- Object::getObjectType(local_5c);
- bVar2 = ObjectType::getFlag((ObjectType *)local_5c,CONTAINER);
- if (bVar2) {
- local_5c[0] = (Object)Obj->ObjectID;
- CloseContainer(local_5c,false);
- }
- local_5c[0] = (Object)Con->ObjectID;
- local_4c[0].ObjectID = local_6c.ObjectID;
- local_3c = (Object)Obj->ObjectID;
- MovementEvent(&local_3c,local_4c,local_5c);
- local_5c[0] = (Object)Obj->ObjectID;
- Object::getContainer(local_4c);
- CollisionEvent(local_5c,local_4c);
- local_5c[0].ObjectID = local_6c.ObjectID;
- local_4c[0] = (Object)Obj->ObjectID;
- NotifyAllCreatures(local_4c,3,local_5c);
- return;
- }
- if (Count != 0) {
- pOVar7 = (Object *)0x2;
- uVar3 = Object::getAttribute(Obj,AMOUNT);
- if (Count <= (int)uVar3) goto LAB_08092800;
- }
- // try { // try from 0809300c to 080930cd has its CatchHandler @ 08093120
- uVar3 = Object::getAttribute(Obj,AMOUNT);
- error(&DAT_080ff700,Count,uVar3);
- goto LAB_08093028;
- }
- Text = "Move: Zielobjekt ist kein Container.\n";
- }
- else {
- Text = &DAT_080ff7e0;
- }
- error(Text);
-LAB_08093028:
- puVar6 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar6 = 0xffffffff;
- // WARNING: Subroutine does not return
- __cxa_throw(puVar6,&RESULT::typeinfo,0);
-}
-
-
-
-void Merge(ulong CreatureID,Object *Obj,Object *Dest,int Count,Object *Ignore)
-
-{
- bool bVar1;
- ulong uVar2;
- ulong uVar3;
- undefined4 *puVar4;
- ulong newCreature;
- char *Text;
- ulong oldCreature;
- Object local_6c [4];
- Object local_5c [4];
- Object local_4c;
- Object Con;
- Object local_3c;
- Object oldCon;
- Object local_2c [7];
-
- // try { // try from 0809314b to 080936c5 has its CatchHandler @ 080936d1
- bVar1 = Object::exists(Obj);
- if (bVar1) {
- bVar1 = Object::exists(Dest);
- if (!bVar1) {
- Text = "Merge: Zielobjekt existiert nicht.\n";
- goto LAB_080936c1;
- }
- Object::getObjectType(local_2c);
- bVar1 = ObjectType::getFlag((ObjectType *)local_2c,CUMULATIVE);
- if ((!bVar1) ||
- ((Count != 0 && (uVar2 = Object::getAttribute(Obj,AMOUNT), Count <= (int)uVar2)))) {
- local_2c[0] = (Object)Dest->ObjectID;
- if (Obj->ObjectID == local_2c[0].ObjectID) {
- return;
- }
- Object::getObjectType(local_2c);
- Object::getObjectType(&local_3c);
- ObjectType::setTypeID((ObjectType *)&local_4c,local_3c.ObjectID);
- if (local_2c[0].ObjectID == local_4c.ObjectID) {
- Object::getObjectType(&local_4c);
- bVar1 = ObjectType::getFlag((ObjectType *)&local_4c,CUMULATIVE);
- if (bVar1) {
- if (Count == -1) {
- Count = Object::getAttribute(Obj,AMOUNT);
- }
- uVar2 = Object::getAttribute(Dest,AMOUNT);
- if (uVar2 + Count < 0x65) {
- Object::getContainer(&local_4c);
- local_3c = (Object)Ignore->ObjectID;
- local_2c[0] = (Object)Obj->ObjectID;
- CheckTopMoveObject(CreatureID,local_2c,&local_3c);
- Object::getObjectType(&local_3c);
- bVar1 = local_3c.ObjectID != 0;
- local_3c = (Object)Obj->ObjectID;
- CheckMoveObject(CreatureID,&local_3c,bVar1);
- Object::getObjectType(&local_3c);
- if (local_3c.ObjectID == 0) {
- local_3c.ObjectID = local_4c.ObjectID;
- local_2c[0] = (Object)Obj->ObjectID;
- CheckMapDestination(CreatureID,local_2c,&local_3c);
- }
- else {
- local_3c.ObjectID = local_4c.ObjectID;
- local_2c[0] = (Object)Obj->ObjectID;
- CheckDepotSpace(CreatureID,local_2c,&local_3c,0);
- local_3c.ObjectID = local_4c.ObjectID;
- uVar2 = GetObjectCreatureID(&local_3c);
- if (uVar2 != 0) {
- local_3c = (Object)Obj->ObjectID;
- CheckWeight(uVar2,&local_3c,Count);
- }
- }
- Object::getContainer(&local_3c);
- local_2c[0] = (Object)Obj->ObjectID;
- uVar2 = GetObjectCreatureID(local_2c);
- Object::getObjectType(local_2c);
- bVar1 = ObjectType::getFlag((ObjectType *)local_2c,MOVEMENTEVENT);
- if (bVar1) {
- Object::getObjectType(local_2c);
- error(&DAT_080ff820,local_2c[0].ObjectID);
- }
- Object::getContainer(local_2c);
- local_5c[0].ObjectID = local_3c.ObjectID;
- local_6c[0].ObjectID = local_3c.ObjectID;
- if (local_2c[0].ObjectID != local_3c.ObjectID) {
- oldCreature = Obj->ObjectID;
- SeparationEvent((Object *)&oldCreature,local_6c);
- }
- uVar3 = Object::getAttribute(Obj,AMOUNT);
- if (Count < (int)uVar3) {
- oldCreature = Obj->ObjectID;
- uVar3 = Object::getAttribute(Obj,AMOUNT);
- ChangeObject((Object *)&oldCreature,AMOUNT,uVar3 - Count);
- oldCreature = Obj->ObjectID;
- AnnounceChangedObject((Object *)&oldCreature,2);
- oldCreature = Obj->ObjectID;
- NotifyTrades((Object *)&oldCreature);
- oldCreature = Dest->ObjectID;
- uVar3 = Object::getAttribute(Dest,AMOUNT);
- ChangeObject((Object *)&oldCreature,AMOUNT,uVar3 + Count);
- }
- else {
- oldCreature = Obj->ObjectID;
- AnnounceChangedObject((Object *)&oldCreature,0);
- oldCreature = Obj->ObjectID;
- NotifyTrades((Object *)&oldCreature);
- oldCreature = Obj->ObjectID;
- NotifyDepot(CreatureID,(Object *)&oldCreature,1);
- oldCreature = Dest->ObjectID;
- local_6c[0] = (Object)Obj->ObjectID;
- MergeObjects(local_6c,(Object *)&oldCreature);
- }
- oldCreature = Dest->ObjectID;
- AnnounceChangedObject((Object *)&oldCreature,2);
- oldCreature = Dest->ObjectID;
- NotifyTrades((Object *)&oldCreature);
- Object::getObjectType((Object *)&oldCreature);
- oldCreature = Dest->ObjectID;
- NotifyCreature(uVar2,(Object *)uVar2,SUB41((Object *)&oldCreature,0));
- Object::getContainer((Object *)&oldCreature);
- Object::getObjectType(local_6c);
- local_6c[0] = (Object)Dest->ObjectID;
- local_5c[0].ObjectID = local_6c[0].ObjectID;
- uVar2 = GetObjectCreatureID(local_5c);
- NotifyCreature(uVar2,(Object *)uVar2,SUB41(local_6c,0));
- oldCreature = Dest->ObjectID;
- Object::getContainer(local_6c);
- CollisionEvent((Object *)&oldCreature,local_6c);
- oldCreature = NONE.ObjectID;
- local_6c[0] = (Object)Dest->ObjectID;
- NotifyAllCreatures(local_6c,2,(Object *)&oldCreature);
- return;
- }
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 0x13;
- }
- else {
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 0x12;
- }
- }
- else {
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 0x11;
- }
- goto LAB_08093635;
- }
- error(&DAT_080ff880,Count);
- }
- else {
- Text = &DAT_080ff900;
-LAB_080936c1:
- error(Text);
- }
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 0xffffffff;
-LAB_08093635:
- // WARNING: Subroutine does not return
- __cxa_throw(puVar4,&RESULT::typeinfo,0);
-}
-
-
-
void Change(Object *Obj,ObjectType *NewType,ulong Value)
{
diff --git a/src/cract.cc b/src/cract.cc
index 22e5b93..d6fd2ee 100644
--- a/src/cract.cc
+++ b/src/cract.cc
@@ -556,7 +556,8 @@ void TCreature::Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){
::Move(this->ID, Obj, DestCon, MoveCount, false, DestObj);
}catch(RESULT r){
// NOTE(fusion): Attempt to exchange inventory items.
- if(DestY < 64 && DestObj != NONE
+ if(DestY >= INVENTORY_FIRST && DestY <= INVENTORY_LAST
+ && DestObj != NONE
&& (r == NOROOM
|| r == HANDSNOTFREE
|| r == HANDBLOCKED
diff --git a/src/operate.cc b/src/operate.cc
index cb4819d..041d663 100644
--- a/src/operate.cc
+++ b/src/operate.cc
@@ -3,6 +3,26 @@
#include "stubs.hh"
+// operate.cc
+void AnnounceMovingCreature(uint32 CreatureID, Object Con);
+void CheckTopMoveObject(uint32 CreatureID, Object Obj, Object Ignore);
+void CheckMoveObject(uint32 CreatureID, Object Obj, bool Take);
+void CheckMapDestination(uint32 CreatureID, Object Obj, Object MapCon);
+void CheckDepotSpace(uint32 CreatureID, Object Source, Object Destination, int Count);
+void CheckWeight(uint32 CreatureID, Object Obj, int Count);
+void NotifyCreature(uint32 CreatureID, Object Obj, bool Inventory);
+void NotifyCreature(uint32 CreatureID, ObjectType Type, bool Inventory);
+//void NotifyAllCreatures(Object Obj, int Type, Object OldCon);
+void NotifyTrades(Object Obj);
+void NotifyDepot(uint32 CreatureID, Object Obj, int Count);
+void CloseContainer(Object Con, bool Force);
+
+// moveuse.cc
+void MovementEvent(Object Obj, Object Start, Object Dest);
+void CollisionEvent(Object Obj, Object Dest);
+void SeparationEvent(Object Obj, Object Start);
+
+
// TODO(fusion): This could have been a simple return value.
void CheckContainerDestination(Object Obj, Object Con){
Object Help = Con;
@@ -81,3 +101,255 @@ void CheckInventoryDestination(Object Obj, Object Con, bool Split){
}
}
}
+
+void Move(uint32 CreatureID, Object Obj, Object Con, int Count, bool NoMerge, Object Ignore){
+ if(!Obj.exists()){
+ error("Move: Übergebenes Objekt existiert nicht.\n");
+ throw ERROR;
+ }
+
+ if(!Con.exists()){
+ int ObjX, ObjY, ObjZ;
+ ObjectType ObjType = Obj.getObjectType();
+ GetObjectCoordinates(Obj, &ObjX, &ObjY, &ObjZ);
+ error("Move: Zielobjekt existiert nicht.\n");
+ error("# Objekt %d an [%d,%d,%d]\n", ObjType.TypeID, ObjX, ObjY, ObjZ);
+ throw ERROR;
+ }
+
+ ObjectType ConType = Con.getObjectType();
+ if(!ConType.getFlag(CONTAINER)){
+ error("Move: Zielobjekt ist kein Container.\n");
+ throw ERROR;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(CUMULATIVE) && (Count == 0 || Count > (int)Obj.getAttribute(AMOUNT))){
+ error("Move: Ungültige Anzahl %d von %d an Teilen bei kumulierbarem Objekt.\n",
+ Count, (int)Obj.getAttribute(AMOUNT));
+ throw ERROR;
+ }
+
+ Object OldCon = Obj.getContainer();
+ if(!NoMerge && ConType.isMapContainer()
+ && ObjType.getFlag(CUMULATIVE)
+ && OldCon != Con){
+ int ConX, ConY, ConZ;
+ GetObjectCoordinates(Con, &ConX, &ConY, &ConZ);
+ Object Top = GetTopObject(ConX, ConY, ConZ, true);
+ if(Top != NONE){
+ try{
+ Merge(CreatureID, Obj, Top, Count, Ignore);
+ return; // <-- EARLY RETURN IF `Merge` SUCCEEDS.
+ }catch(RESULT r){
+ if(r == DESTROYED){
+ throw;
+ }
+ }
+ }
+ }
+
+ // TODO(fusion): The only event inside `Merge` that could modify the object
+ // and still throw some non `DESTROYED` error is the separation event.
+ // From what I've seen, it is used exclusively with non CUMULATIVE objects
+ // like depot switches to perform "step out" events but using them with
+ // CUMULATIVE objects could be problematic as the event is triggered on the
+ // whole object which includes both the remainder and moved parts, meaning
+ // that transforming the object then would already make the merge "invalid".
+
+ if(!Obj.exists()){
+ error("Move: Übergebenes Objekt existiert nicht mehr.\n");
+ }
+
+ int ObjCount = 1;
+ if(ObjType.getFlag(CUMULATIVE)){
+ ObjCount = (int)Obj.getAttribute(AMOUNT);
+ }
+
+ if(Count == -1){
+ Count = ObjCount;
+ }
+
+ bool Split = Count < ObjCount;
+ uint32 ObjOwnerID = GetObjectCreatureID(Obj);
+ uint32 ConOwnerID = GetObjectCreatureID(Con);
+ CheckTopMoveObject(CreatureID, Obj, Ignore);
+ if(ConType.isMapContainer()){
+ CheckMoveObject(CreatureID, Obj, false);
+ CheckMapDestination(CreatureID, Obj, Con);
+ }else{
+ CheckMoveObject(CreatureID, Obj, true);
+ if(ConType.isBodyContainer()){
+ CheckInventoryDestination(Obj, Con, Split);
+ }else{
+ CheckContainerDestination(Obj, Con);
+ }
+
+ if(Split){
+ CheckDepotSpace(CreatureID, NONE, Con, 1);
+ }else{
+ CheckDepotSpace(CreatureID, Obj, Con, CountObjects(Obj));
+ }
+
+ if(ConOwnerID != 0){
+ CheckWeight(ConOwnerID, Obj, Count);
+ }
+ }
+
+ // NOTE(fusion): `Obj` becomes the split part while `Remainder`, the remainder.
+ Object Remainder = NONE;
+ if(Split){
+ Remainder = Obj;
+ Obj = SplitObject(Obj, Count);
+ }
+
+ if(OldCon != Con){
+ SeparationEvent(Obj, OldCon);
+ }
+
+ // TODO(fusion): There is surely a way to reduce these creature checks?
+ // perhaps split them into two linear paths, with a few duplications but
+ // easier to read.
+
+ if(!ObjType.isCreatureContainer() && Remainder == NONE){
+ AnnounceChangedObject(Obj, 0); // OBJECT_DISAPPEAR ?
+ NotifyTrades(Obj);
+ NotifyDepot(CreatureID, Obj, CountObjects(Obj));
+ }
+
+ // TODO(fusion): This could probably be moved to the end of the function
+ // along with the other `CloseContainer`.
+ if(ObjType.getFlag(CONTAINER) && CreatureID == 0){
+ CloseContainer(Obj, true);
+ }
+
+ if(ObjType.isCreatureContainer()){
+ uint32 MovingCreatureID = Obj.getCreatureID();
+ TCreature *Creature = GetCreature(MovingCreatureID);
+ if(Creature != NULL){
+ Creature->NotifyTurn(Con);
+ }else{
+ error("Move: Kreatur mit ungültiger ID %d.\n", MovingCreatureID);
+ }
+ AnnounceMovingCreature(MovingCreatureID, Con);
+ }
+
+ MoveObject(Obj, Con);
+
+ if(!ObjType.isCreatureContainer()){
+ AnnounceChangedObject(Obj, 1); // OBJECT_APPEAR
+ NotifyTrades(Obj);
+ NotifyDepot(CreatureID, Obj, -CountObjects(Obj));
+ }
+
+ if(Remainder != NONE){
+ AnnounceChangedObject(Remainder, 2); // OBJECT_CHANGED
+ NotifyTrades(Remainder);
+ }
+
+ if(ObjType.isCreatureContainer()){
+ uint32 MovingCreatureID = Obj.getCreatureID();
+ TCreature *Creature = GetCreature(MovingCreatureID);
+ if(Creature != NULL){
+ Creature->NotifyGo();
+ }else{
+ error("Move: Kreatur mit ungültiger ID %d.\n", MovingCreatureID);
+ }
+ }
+
+ NotifyCreature(ObjOwnerID, Obj, OldCon.getObjectType().isBodyContainer());
+ NotifyCreature(ConOwnerID, Obj, Con.getObjectType().isBodyContainer());
+
+ if(ObjType.getFlag(CONTAINER)){
+ CloseContainer(Obj, false);
+ }
+
+ MovementEvent(Obj, OldCon, Con);
+ CollisionEvent(Obj, Con);
+ NotifyAllCreatures(Obj, 3, OldCon); // CREATURE_MOVE_STIMULUS_ ?
+}
+
+void Merge(uint32 CreatureID, Object Obj, Object Dest, int Count, Object Ignore){
+ if(!Obj.exists()){
+ error("Merge: Übergebenes Objekt existiert nicht.\n");
+ throw ERROR;
+ }
+
+ if(!Dest.exists()){
+ error("Merge: Zielobjekt existiert nicht.\n");
+ throw ERROR;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.getFlag(CUMULATIVE) && (Count == 0 || Count > (int)Obj.getAttribute(AMOUNT))){
+ error("Merge: Ungültige Anzahl %d an Teilen bei kumulierbarem Objekt.\n", Count);
+ throw ERROR;
+ }
+
+ if(Obj == Dest){
+ return;
+ }
+
+ if(ObjType != Dest.getObjectType()){
+ throw NOMATCH;
+ }
+
+ if(!ObjType.getFlag(CUMULATIVE)){
+ throw NOTCUMULABLE;
+ }
+
+ int ObjCount = Obj.getAttribute(AMOUNT);
+ int DestCount = Dest.getAttribute(AMOUNT);
+ if(Count == -1){
+ Count = ObjCount;
+ }
+
+ if((Count + DestCount) > 100){
+ throw TOOMANYPARTS;
+ }
+
+ Object DestCon = Dest.getContainer();
+ uint32 DestOwnerID = GetObjectCreatureID(Dest);
+ CheckTopMoveObject(CreatureID, Obj, Ignore);
+ if(DestCon.getObjectType().isMapContainer()){
+ CheckMoveObject(CreatureID, Obj, false);
+ CheckMapDestination(CreatureID, Obj, DestCon);
+ }else{
+ CheckMoveObject(CreatureID, Obj, true);
+ CheckDepotSpace(CreatureID, Obj, DestCon, 0);
+ if(DestOwnerID != 0){
+ CheckWeight(DestOwnerID, Obj, Count);
+ }
+ }
+
+ Object ObjCon = Obj.getContainer();
+ uint32 ObjOwnerID = GetObjectCreatureID(Obj);
+
+ // TODO(fusion): What is even going on here?
+ if(ObjType.getFlag(MOVEMENTEVENT)){
+ error("Merge: Movement-Event für kumulierbares Objekt %d wird nicht ausgelöst.\n", ObjType.TypeID);
+ }
+
+ if(ObjCon != DestCon){
+ SeparationEvent(Obj, ObjCon);
+ }
+
+ if(Count < ObjCount){
+ ChangeObject(Obj, AMOUNT, ObjCount - Count);
+ AnnounceChangedObject(Obj, 2); // OBJECT_CHANGED ?
+ NotifyTrades(Obj);
+ ChangeObject(Dest, AMOUNT, DestCount + Count);
+ }else{
+ AnnounceChangedObject(Obj, 0); // OBJECT_DISAPPEAR ?
+ NotifyTrades(Obj);
+ NotifyDepot(CreatureID, Obj, 1);
+ MergeObjects(Obj, Dest);
+ }
+
+ AnnounceChangedObject(Dest, 2); // OBJECT_CHANGED ?
+ NotifyTrades(Dest);
+ NotifyCreature(ObjOwnerID, Dest, ObjCon.getObjectType().isBodyContainer());
+ NotifyCreature(DestOwnerID, Dest, DestCon.getObjectType().isBodyContainer());
+ CollisionEvent(Dest, DestCon);
+ NotifyAllCreatures(Dest, 2, NONE); // CREATURE_MOVE_STIMULUS_ ?
+}