aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--reference/game.c483
-rw-r--r--src/cr.hh4
-rw-r--r--src/cract.cc184
-rw-r--r--src/crcombat.cc15
-rw-r--r--src/crmain.cc22
-rw-r--r--src/enums.hh39
-rw-r--r--src/magic.cc16
-rw-r--r--src/objects.hh22
-rw-r--r--src/operate.cc82
-rw-r--r--src/operate.hh10
-rw-r--r--src/stubs.hh4
12 files changed, 367 insertions, 522 deletions
diff --git a/Makefile b/Makefile
index e3acc48..88234fe 100644
--- a/Makefile
+++ b/Makefile
@@ -13,9 +13,9 @@ else
CFLAGS += -O2
endif
-HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/cr.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/objects.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh
+HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/cr.hh $(SRCDIR)/enums.hh $(SRCDIR)/info.hh $(SRCDIR)/magic.hh $(SRCDIR)/map.hh $(SRCDIR)/objects.hh $(SRCDIR)/operate.hh $(SRCDIR)/script.hh $(SRCDIR)/stubs.hh $(SRCDIR)/thread.hh
-$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/cract.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/crmain.obj $(BUILDDIR)/crplayer.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj
+$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/cract.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/crmain.obj $(BUILDDIR)/crplayer.obj $(BUILDDIR)/crskill.obj $(BUILDDIR)/info.obj $(BUILDDIR)/magic.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/operate.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/strings.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj
$(CC) -c $(CFLAGS) $(LFLAGS) -o $@ $^
$(BUILDDIR)/config.obj: $(SRCDIR)/config.cc $(HEADERS)
@@ -62,6 +62,10 @@ $(BUILDDIR)/objects.obj: $(SRCDIR)/objects.cc $(HEADERS)
@mkdir -p $(@D)
$(CC) -c $(CFLAGS) -o $@ $<
+$(BUILDDIR)/operate.obj: $(SRCDIR)/operate.cc $(HEADERS)
+ @mkdir -p $(@D)
+ $(CC) -c $(CFLAGS) -o $@ $<
+
$(BUILDDIR)/script.obj: $(SRCDIR)/script.cc $(HEADERS)
@mkdir -p $(@D)
$(CC) -c $(CFLAGS) -o $@ $<
diff --git a/reference/game.c b/reference/game.c
index fec668b..7e6d16d 100644
--- a/reference/game.c
+++ b/reference/game.c
@@ -22210,58 +22210,6 @@ LAB_0808ffd8:
__cxa_throw(puVar3,&RESULT::typeinfo,0);
}
-
-
-void CheckContainerDestination(Object *Obj,Object *Con)
-
-{
- bool bVar1;
- undefined4 *puVar2;
- int iVar3;
- ulong uVar4;
- ulong *puVar5;
- Object local_4c [4];
- Object local_3c [4];
- ulong local_2c;
- Object Help;
-
- local_2c = Con->ObjectID;
- do {
- puVar5 = &local_2c;
- // try { // try from 08090164 to 08090227 has its CatchHandler @ 08090243
- Object::getObjectType(local_3c);
- if (local_3c[0].ObjectID == 0) {
- Object::getObjectType(local_4c);
- bVar1 = ObjectType::getFlag((ObjectType *)local_4c,CHEST);
- if (!bVar1) {
- local_4c[0] = (Object)Con->ObjectID;
- iVar3 = CountObjectsInContainer(local_4c);
- Object::getObjectType(local_3c);
- uVar4 = ObjectType::getAttribute((ObjectType *)local_3c,CAPACITY);
- if ((int)uVar4 <= iVar3) {
- puVar2 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar2 = 10;
-LAB_080901af:
- // WARNING: Subroutine does not return
- __cxa_throw(puVar2,&RESULT::typeinfo,0);
- }
- }
- return;
- }
- local_3c[0] = (Object)Obj->ObjectID;
- if (local_2c == local_3c[0].ObjectID) {
- puVar2 = (undefined4 *)__cxa_allocate_exception(4,puVar5);
- *puVar2 = 9;
- goto LAB_080901af;
- }
- Object::getContainer(local_4c);
- local_3c[0].ObjectID = local_4c[0].ObjectID;
- local_2c = local_4c[0].ObjectID;
- } while( true );
-}
-
-
-
void CheckContainerPlace(ObjectType *Type,Object *Con,Object *oldObj)
{
@@ -22388,175 +22336,6 @@ LAB_080904cb:
__cxa_throw(puVar3,&RESULT::typeinfo,0);
}
-
-
-void CheckInventoryDestination(Object *Obj,Object *Con,bool Split)
-
-{
- uint uVar1;
- Object OVar2;
- bool bVar3;
- int ClothesType;
- ulong uVar4;
- ulong uVar5;
- ulong uVar6;
- undefined4 *puVar7;
- int Position;
- bool bVar8;
- undefined4 uVar9;
- int local_68;
- int Hand;
- ulong uStack_5c;
- bool Split_local;
- Object Help;
- Object local_4c [4];
- ulong local_3c;
- Object local_2c;
- Object Help_1;
-
- local_2c = (Object)Con->ObjectID;
- // try { // try from 08090584 to 08090930 has its CatchHandler @ 080909c5
- uVar4 = GetObjectBodyPosition(&local_2c);
- uVar1 = uVar4 - 5;
- if ((1 < uVar1) && (uVar4 != 10)) {
- Object::getObjectType(&local_2c);
- bVar3 = ObjectType::getFlag((ObjectType *)&local_2c,CLOTHES);
- if (!bVar3) {
- puVar7 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar7 = 0xb;
- goto LAB_0809079e;
- }
- Object::getObjectType(&local_2c);
- uVar5 = ObjectType::getAttribute((ObjectType *)&local_2c,BODYPOSITION);
- if (uVar5 == 0) {
- puVar7 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar7 = 0xc;
- goto LAB_0809079e;
- }
- if (uVar4 != uVar5) {
- puVar7 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar7 = 0xd;
- goto LAB_0809079e;
- }
- }
- local_2c = (Object)Con->ObjectID;
- uVar5 = GetObjectCreatureID(&local_2c);
- Object::getObjectType(&local_2c);
- bVar3 = ObjectType::getFlag((ObjectType *)&local_2c,CLOTHES);
- if (bVar3) {
- Object::getObjectType(&local_2c);
- uVar6 = ObjectType::getAttribute((ObjectType *)&local_2c,BODYPOSITION);
- if ((uVar6 == 0) && (uVar1 < 2)) {
- uVar9 = 5;
- uVar4 = uVar5;
- GetBodyObject((ulong)&local_2c,uVar5);
- bVar3 = false;
- local_3c = NONE.ObjectID;
- local_4c[0].ObjectID = NONE.ObjectID;
- if ((local_2c.ObjectID != NONE.ObjectID) &&
- (uStack_5c = Obj->ObjectID, local_4c[0].ObjectID = uStack_5c,
- local_2c.ObjectID != uStack_5c)) {
- bVar3 = true;
- }
- if (!bVar3) {
- uVar9 = 6;
- GetBodyObject((ulong)local_4c,uVar5);
- OVar2.ObjectID = local_4c[0].ObjectID;
- bVar3 = false;
- local_2c.ObjectID = local_4c[0].ObjectID;
- bVar8 = local_4c[0].ObjectID != NONE.ObjectID;
- uStack_5c = NONE.ObjectID;
- local_4c[0].ObjectID = NONE.ObjectID;
- if ((bVar8) &&
- (local_4c[0] = (Object)Obj->ObjectID, local_3c = local_4c[0].ObjectID,
- OVar2.ObjectID != local_4c[0].ObjectID)) {
- bVar3 = true;
- }
- uVar4 = uVar5;
- if (!bVar3) {
- return;
- }
- }
- puVar7 = (undefined4 *)__cxa_allocate_exception(4,uVar4,uVar9);
- *puVar7 = 0xe;
- goto LAB_0809079e;
- }
- }
- uVar6 = uVar5;
- GetBodyObject((ulong)&stack0xffffffa4,uVar5);
- local_4c[0].ObjectID = NONE.ObjectID;
- local_3c = NONE.ObjectID;
- if (uStack_5c == NONE.ObjectID) {
- if (uVar1 < 2) {
- local_68 = 5;
- do {
- GetBodyObject((ulong)&stack0xffffffa4,uVar5);
- local_4c[0].ObjectID = NONE.ObjectID;
- if (uStack_5c != NONE.ObjectID) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,CLOTHES);
- if (bVar3) {
- Object::getObjectType(local_4c);
- uVar4 = ObjectType::getAttribute((ObjectType *)local_4c,BODYPOSITION);
- if (uVar4 == 0) {
- puVar7 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar7 = 0xf;
- goto LAB_0809079e;
- }
- }
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,WEAPON);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,THROW);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,BOW);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,WAND);
- if (!bVar3) goto LAB_080906c2;
- }
- }
- }
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,WEAPON);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,THROW);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,BOW);
- if (!bVar3) {
- Object::getObjectType(local_4c);
- bVar3 = ObjectType::getFlag((ObjectType *)local_4c,WAND);
- if (!bVar3) goto LAB_080906c2;
- }
- }
- }
- if ((Split) ||
- (local_4c[0] = (Object)Obj->ObjectID, local_3c = local_4c[0].ObjectID,
- uStack_5c != local_4c[0].ObjectID)) {
- puVar7 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar7 = 0x10;
- goto LAB_0809079e;
- }
- }
-LAB_080906c2:
- local_68 = local_68 + 1;
- } while (local_68 < 7);
- }
- return;
- }
- puVar7 = (undefined4 *)__cxa_allocate_exception(4,uVar6,uVar4);
- *puVar7 = 4;
-LAB_0809079e:
- // WARNING: Subroutine does not return
- __cxa_throw(puVar7,&RESULT::typeinfo,0);
-}
-
-
-
void CheckInventoryPlace(ObjectType *Type,Object *Con,Object *OldObj)
{
@@ -43304,268 +43083,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority
return;
}
-// DWARF original prototype: void Move(TCreature * this, Object * Obj, int zx, int zy, int zz, uchar
-// Count)
-
-void __thiscall TCreature::Move(TCreature *this,Object *Obj,int zx,int zy,int zz,uchar Count)
-
-{
- bool bVar1;
- bool bVar2;
- ulong uVar3;
- undefined4 *puVar4;
- int *piVar5;
- int Delay;
- uint Count_00;
- Object *Obj_00;
- Object *pOVar6;
- int iVar7;
- int local_a0;
- int b_1;
- int b;
- int iStack_94;
- uchar Count_local;
- int local_90;
- Object local_8c [4];
- Object local_7c [4];
- Object local_6c;
- Object DestCon;
- Object local_5c;
- Object DestObj;
- Object local_4c [4];
- Object local_3c [4];
- Object local_2c;
- Object Dest;
-
- b._3_1_ = Count;
- // try { // try from 080bf7d5 to 080bfb3a has its CatchHandler @ 080c01e0
- bVar1 = Object::exists(Obj);
- if (bVar1) {
- local_2c.ObjectID = (this->CrObject).ObjectID;
- if (Obj->ObjectID == local_2c.ObjectID) {
- Go(this,zx,zy,zz);
- return;
- }
- Object::getObjectType(&local_2c);
- if (local_2c.ObjectID == 99) {
- TCombat::DelayAttack(&this->Combat,2000);
- }
- Object::getObjectType(&local_2c);
- bVar1 = ObjectType::getFlag((ObjectType *)&local_2c,CUMULATIVE);
- // try { // try from 080bfff5 to 080c0037 has its CatchHandler @ 080c01e0
- if ((bVar1) && (uVar3 = Object::getAttribute(Obj,AMOUNT), uVar3 < Count)) {
- uVar3 = Object::getAttribute(Obj,AMOUNT);
- b._3_1_ = (byte)uVar3;
- }
- if (zx == 0xffff) {
- if (zy == 0) {
- b_1 = 1;
- iVar7 = b_1;
- do {
- b_1 = iVar7;
- local_6c = (Object)Obj->ObjectID;
- // try { // try from 080bfb73 to 080bfb94 has its CatchHandler @ 080c003d
- GetBodyContainer((ulong)&local_5c,this->ID);
- CheckInventoryDestination(&local_6c,&local_5c,false);
- if ((1 < b_1 - 5U) && (b_1 != 10)) break;
- iVar7 = b_1 + 1;
- } while (b_1 + 1 < 0xb);
- zy = b_1;
- if (b_1 == 0) {
- local_a0 = 1;
- do {
- pOVar6 = (Object *)this->ID;
- iVar7 = local_a0;
- // try { // try from 080bfbe3 to 080bfc4b has its CatchHandler @ 080c01e0
- GetBodyObject((ulong)&local_6c,(int)pOVar6);
- local_5c.ObjectID = ::NONE.ObjectID;
- if (local_6c.ObjectID != ::NONE.ObjectID) {
- Object::getObjectType(&local_5c);
- pOVar6 = (Object *)0x4;
- bVar1 = ObjectType::getFlag((ObjectType *)&local_5c,CONTAINER);
- if (bVar1) {
- pOVar6 = &local_5c;
- local_5c.ObjectID = local_6c.ObjectID;
- local_4c[0] = (Object)Obj->ObjectID;
- // try { // try from 080bffcb to 080bffcf has its CatchHandler @ 080c0067
- CheckContainerDestination(local_4c,pOVar6);
- b_1 = local_a0;
- }
- }
- local_a0 = local_a0 + 1;
- } while (local_a0 < 0xb);
- zy = b_1;
- if (b_1 == 0) {
- puVar4 = (undefined4 *)__cxa_allocate_exception(4,pOVar6,iVar7);
- *puVar4 = 4;
- goto LAB_080bfb23;
- }
- }
- }
- if (((0 < zy) && (0x34 < zy - 0xbU)) && (zy < 0x50)) {
- pOVar6 = &local_6c;
- Obj_00 = (Object *)this->ID;
- GetBodyContainer((ulong)pOVar6,(int)Obj_00);
- local_5c.ObjectID = ::NONE.ObjectID;
- if (zy < 0x40) {
- Obj_00 = (Object *)this->ID;
- GetBodyObject((ulong)local_3c,(int)Obj_00);
- local_5c.ObjectID = local_3c[0].ObjectID;
- }
- else {
- if (zz < 0xfe) {
- ObjectType::setTypeID((ObjectType *)&local_2c,0);
- Obj_00 = (Object *)this->ID;
- GetObject((ulong)local_3c,(int)Obj_00,0xffff,zy,zz,(ObjectType *)zz);
- local_5c.ObjectID = local_3c[0].ObjectID;
- }
- if (zz == 0xfe) {
- local_4c[0].ObjectID = ::NONE.ObjectID;
- if (local_6c.ObjectID == ::NONE.ObjectID) goto LAB_080bfeb5;
- Object::getContainer(local_3c);
- local_6c.ObjectID = local_3c[0].ObjectID;
- Obj_00 = pOVar6;
- }
- }
- bVar1 = false;
- if (local_5c.ObjectID != ::NONE.ObjectID) {
- local_4c[0].ObjectID = ::NONE.ObjectID;
- local_3c[0].ObjectID = ::NONE.ObjectID;
- Object::getObjectType(local_3c);
- Obj_00 = (Object *)0x4;
- bVar1 = ObjectType::getFlag((ObjectType *)local_3c,CONTAINER);
- }
- if (bVar1) {
- local_6c.ObjectID = local_5c.ObjectID;
- local_5c.ObjectID = ::NONE.ObjectID;
- local_2c.ObjectID = ::NONE.ObjectID;
- }
- bVar1 = false;
- local_4c[0].ObjectID = ::NONE.ObjectID;
- local_3c[0].ObjectID = ::NONE.ObjectID;
- if (local_5c.ObjectID != ::NONE.ObjectID) {
- Object::getObjectType(local_3c);
- Obj_00 = (Object *)0x6;
- bVar2 = ObjectType::getFlag((ObjectType *)local_3c,CUMULATIVE);
- if (bVar2) {
- Object::getObjectType(local_3c);
- Obj_00 = &local_5c;
- Object::getObjectType(&local_2c);
- if (local_3c[0].ObjectID == local_2c.ObjectID) {
- bVar1 = true;
- }
- }
- }
- if (bVar1) {
- Count_00 = (uint)b._3_1_;
- Obj_00 = (Object *)0x2;
- // try { // try from 080bfd46 to 080bfd4a has its CatchHandler @ 080c01e0
- uVar3 = Object::getAttribute(&local_5c,AMOUNT);
- if (100 < uVar3 + Count_00) {
- Obj_00 = (Object *)0x2;
- // try { // try from 080bfdd1 to 080bffa7 has its CatchHandler @ 080c01e0
- uVar3 = Object::getAttribute(&local_5c,AMOUNT);
- Count_00 = 100 - uVar3;
- }
- if (0 < (int)Count_00) {
- Obj_00 = local_8c;
- local_2c.ObjectID = ::NONE.ObjectID;
- local_7c[0].ObjectID = local_5c.ObjectID;
- local_8c[0] = (Object)Obj->ObjectID;
- // try { // try from 080bfd98 to 080bfd9c has its CatchHandler @ 080c0082
- Merge(this->ID,Obj_00,local_7c,Count_00,&local_2c);
- b._3_1_ = b._3_1_ - (char)Count_00;
- if (b._3_1_ == 0) {
- return;
- }
- }
- local_7c[0] = (Object)::NONE.ObjectID;
- local_5c.ObjectID = local_7c[0].ObjectID;
- }
- local_8c[0].ObjectID = ::NONE.ObjectID;
- if (local_6c.ObjectID != ::NONE.ObjectID) {
- local_7c[0].ObjectID = local_6c.ObjectID;
- local_8c[0].ObjectID = local_5c.ObjectID;
- local_4c[0] = (Object)Obj->ObjectID;
- // try { // try from 080bfd13 to 080bfd17 has its CatchHandler @ 080c00b2
- ::Move(this->ID,local_4c,local_7c,(uint)b._3_1_,false,local_8c);
- return;
- }
- puVar4 = (undefined4 *)__cxa_allocate_exception(4,Obj_00);
- *puVar4 = 5;
- goto LAB_080bfb23;
- }
- error(&DAT_08106e00,zy);
- }
- else {
- GetMapContainer((int)&local_2c,zx,zy);
- bVar1 = Object::exists(&local_2c);
- if (bVar1) {
- Object::getObjectType(local_3c);
- bVar1 = ObjectType::getFlag((ObjectType *)local_3c,HANG);
- if (bVar1) {
- bVar1 = false;
- bVar2 = CoordinateFlag();
- if ((bVar2) || (bVar2 = CoordinateFlag(), bVar2)) {
- bVar1 = true;
- }
- if (bVar1) {
- local_3c[0].ObjectID = local_2c.ObjectID;
- bVar1 = ObjectInRange(this->ID,local_3c,1);
- if (!bVar1) {
- bVar1 = ToDoClear(this);
- if ((bVar1) && (this->Type == PLAYER)) {
- SendSnapback(this->Connection);
- }
- local_4c[0] = (Object)Obj->ObjectID;
- uVar3 = GetObjectCreatureID(local_4c);
- if (uVar3 != this->ID) {
- local_5c = (Object)Obj->ObjectID;
- ToDoMove(this,&local_5c,0xffff,0,0,'\x01');
- }
- ToDoGo(this,zx,zy,zz,false,0x7fffffff);
- local_5c = (Object)Obj->ObjectID;
- ToDoMove(this,&local_5c,zx,zy,zz,'\x01');
- if (this->NrToDo == 0) {
- return;
- }
- this->LockToDo = true;
- this->ActToDo = 0;
- local_90 = CalculateDelay(this);
- iStack_94 = 1;
- piVar5 = &stack0xffffff6c;
- if (0 < local_90) {
- piVar5 = &local_90;
- }
- iVar7 = *piVar5;
- priority_queue<>::insert(&ToDoQueue);
- this->NextWakeup = iVar7 + ServerMilliseconds;
- return;
- }
- }
- }
- local_5c.ObjectID = ::NONE.ObjectID;
- local_4c[0].ObjectID = local_2c.ObjectID;
- local_6c = (Object)Obj->ObjectID;
- ::Move(this->ID,&local_6c,local_4c,(uint)b._3_1_,false,&local_5c);
- return;
- }
- }
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 0xffffffff;
- }
- else {
-LAB_080bfeb5:
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = 1;
- }
-LAB_080bfb23:
- // WARNING: Subroutine does not return
- __cxa_throw(puVar4,&RESULT::typeinfo,0);
-}
-
-
-
// DWARF original prototype: void Trade(TCreature * this, Object * Obj, ulong PartnerID)
void __thiscall TCreature::Trade(TCreature *this,Object *Obj,ulong PartnerID)
diff --git a/src/cr.hh b/src/cr.hh
index 37a9f3d..2c60163 100644
--- a/src/cr.hh
+++ b/src/cr.hh
@@ -499,6 +499,10 @@ struct TCreature: TSkillBase {
void Go(int DestX, int DestY, int DestZ);
void Rotate(int Direction);
void Rotate(TCreature *Target);
+ void Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count);
+ void Trade(Object Obj, uint32 TradePartner);
+ void Use(Object Obj1, Object Obj2, uint8 Dummy);
+ void Turn(Object Obj);
void Attack(void);
void Execute(void);
uint32 CalculateDelay(void);
diff --git a/src/cract.cc b/src/cract.cc
index c4e85b6..8306ad5 100644
--- a/src/cract.cc
+++ b/src/cract.cc
@@ -1,4 +1,5 @@
#include "cr.hh"
+#include "operate.hh"
#include "stubs.hh"
@@ -387,7 +388,7 @@ void TCreature::Go(int DestX, int DestY, int DestZ){
}
Object Dest = GetMapContainer(DestX, DestY, DestZ);
- Move(this->ID, this->CrObject, Dest, -1, false, NONE);
+ ::Move(this->ID, this->CrObject, Dest, -1, false, NONE);
}
void TCreature::Rotate(int Direction){
@@ -415,6 +416,185 @@ void TCreature::Rotate(TCreature *Target){
this->Rotate(Direction);
}
+void TCreature::Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){
+ // NOTE(fusion): What a disaster.
+
+ if(!Obj.exists()){
+ throw NOTACCESSIBLE;
+ }
+
+ if(Obj == this->CrObject){
+ this->Go(DestX, DestY, DestZ);
+ return;
+ }
+
+ ObjectType ObjType = Obj.getObjectType();
+ if(ObjType.isCreatureContainer()){
+ this->Combat.DelayAttack(2000);
+ }
+
+ int MoveCount = (int)Count;
+ if(ObjType.getFlag(CUMULATIVE)){
+ int ObjAmount = (int)Obj.getAttribute(AMOUNT);
+ if(MoveCount > ObjAmount){
+ MoveCount = ObjAmount;
+ }
+ }
+
+ if(DestX == 0xFFFF){ // SPECIAL_COORDINATE ?
+ // NOTE(fusion): Any inventory slot.
+ if(DestY == 0){
+ // NOTE(fusion): `CheckInventoryDestination` will throw if it's not
+ // possible to place the object on the chosen container. We want to
+ // find a slot that doesn't make it throw while giving priority to
+ // non hand or ammo slots.
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
+ try{
+ Object Con = GetBodyContainer(this->ID, Position);
+ CheckInventoryDestination(Obj, Con, false);
+ }catch(...){
+ continue;
+ }
+
+ DestY = Position;
+ if(Position != INVENTORY_RIGHTHAND
+ && Position != INVENTORY_LEFTHAND
+ && Position != INVENTORY_AMMO){
+ break;
+ }
+ }
+
+ // NOTE(fusion): No appropriate inventory slot was found so we now
+ // fallback to inventory containers. For whatever reason we don't
+ // give priority to the bag slot.
+ if(DestY == 0){
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
+ Object Con = GetBodyObject(this->ID, Position);
+ if(Con != NONE && Con.getObjectType().getFlag(CONTAINER)){
+ try{
+ CheckContainerDestination(Obj, Con);
+ }catch(RESULT r){
+ continue;
+ }
+
+ DestY = Position;
+ }
+ }
+ }
+
+ if(DestY == 0){
+ throw NOROOM;
+ }
+ }
+
+ Object DestCon = NONE;
+ Object DestObj = NONE;
+ if(DestY >= INVENTORY_FIRST && DestY <= INVENTORY_LAST){
+ DestCon = GetBodyContainer(this->ID, DestY);
+ DestObj = GetBodyObject(this->ID, DestY);
+ }else if(DestY >= 64 && DestY < 80){
+ DestCon = GetBodyContainer(this->ID, DestY);
+ if(DestZ < 254){
+ // TODO(fusion): The last argument to `GetObject` is the object
+ // type we expect to find and it seems that the type id of a map
+ // container (which is 0) can be used as a wildcard for any object
+ // it finds.
+ DestObj = GetObject(this->ID, DestX, DestY, DestZ, DestZ, 0);
+ }else if(DestZ == 254){
+ if(DestCon == NONE){
+ throw NOTACCESSIBLE;
+ }
+ DestCon = DestCon.getContainer();
+ }
+ }else{
+ error("TCreature::Move: Ungültiger Containercode %d.\n", DestY);
+ throw ERROR;
+ }
+
+ if(DestObj != NONE){
+ ObjectType DestObjType = DestObj.getObjectType();
+ if(DestObjType.getFlag(CONTAINER)){
+ DestCon = DestObj;
+ DestObj = NONE;
+ }else if(DestObjType.getFlag(CUMULATIVE) && DestObjType == ObjType){
+ int DestAmount = (int)DestObj.getAttribute(AMOUNT);
+ int MergeCount = MoveCount;
+ if((DestAmount + MergeCount) > 100){
+ MergeCount = 100 - DestAmount;
+ }
+
+ if(MergeCount > 0){
+ try{
+ ::Merge(this->ID, Obj, DestObj, MergeCount, NONE);
+ MoveCount -= MergeCount;
+ if(MoveCount <= 0){
+ return;
+ }
+ }catch(RESULT r){
+ if(r == TOOHEAVY){
+ throw;
+ }
+ }
+ }
+
+ DestObj = NONE;
+ }
+ }
+
+ if(DestCon == NONE){
+ throw OUTOFRANGE;
+ }
+
+ try{
+ // TODO(fusion): Find out what is this last parameter to `Move`.
+ ::Move(this->ID, Obj, DestCon, MoveCount, false, DestObj);
+ }catch(RESULT r){
+ // NOTE(fusion): Attempt to exchange inventory items.
+ if(DestY < 64 && DestObj != NONE
+ && (r == NOROOM
+ || r == HANDSNOTFREE
+ || r == HANDBLOCKED
+ || r == ONEWEAPONONLY)){
+ Object ObjCon = Obj.getContainer();
+ ::Move(this->ID, DestObj, ObjCon, -1, false, NONE);
+ ::Move(this->ID, Obj, DestCon, MoveCount, false, NONE);
+ }else{
+ throw;
+ }
+ }
+ }else{
+ Object Dest = GetMapContainer(DestX, DestY, DestZ);
+ if(!Dest.exists()){
+ throw ERROR;
+ }
+
+ if(ObjType.getFlag(HANG)
+ && (CoordinateFlag(DestX, DestY, DestZ, HOOKSOUTH)
+ || CoordinateFlag(DestY, DestY, DestZ, HOOKEAST))
+ && !ObjectInRange(this->ID, Dest, 1)){
+ if(this->ToDoClear() && this->Type == PLAYER){
+ SendSnapback(this->Connection);
+ }
+
+ // NOTE(fusion): Pick up object if it's not in our inventory.
+ if(GetObjectCreatureID(Obj) != this->ID){
+ this->ToDoMove(Obj, 0xFFFF, 0, 0, 1);
+ }
+
+ // NOTE(fusion): Walk to destination then try again.
+ this->ToDoGo(DestX, DestY, DestZ, false, INT_MAX);
+ this->ToDoMove(Obj, DestX, DestY, DestZ, 1);
+ this->ToDoStart();
+ }else{
+ ::Move(this->ID, Obj, Dest, MoveCount, false, NONE);
+ }
+ }
+}
+
void TCreature::Attack(void){
this->Combat.Attack();
}
@@ -460,7 +640,7 @@ void TCreature::Execute(void){
}
case TDMove:{
- this->Move(TD.Move.Obj, TD.Move.x, TD.Move.y, TD.Move.z, TD.Move.Count);
+ this->Move(TD.Move.Obj, TD.Move.x, TD.Move.y, TD.Move.z, (uint8)TD.Move.Count);
break;
}
diff --git a/src/crcombat.cc b/src/crcombat.cc
index dd732e3..25fff0e 100644
--- a/src/crcombat.cc
+++ b/src/crcombat.cc
@@ -48,10 +48,10 @@ void TCombat::GetWeapon(void){
return;
}
- // TODO(fusion): We're iterating over left and right hand slots. Make
- // it clearer perhaps.
- for(int Hand = 5; Hand <= 6; Hand += 1){
- Object Obj = GetBodyObject(Master->ID, Hand);
+ for(int Position = INVENTORY_HAND_FIRST;
+ Position <= INVENTORY_HAND_LAST;
+ Position += 1){
+ Object Obj = GetBodyObject(Master->ID, Position);
if(Obj == NONE){
continue;
}
@@ -111,7 +111,7 @@ void TCombat::GetAmmo(void){
}
// TODO(fusion): Check if `Master` is NULL?
- Object Ammo = GetBodyObject(this->Master->ID, 10);
+ Object Ammo = GetBodyObject(this->Master->ID, INVENTORY_AMMO);
this->Ammo = NONE;
if(Ammo != NONE){
ObjectType AmmoType = Ammo.getObjectType();
@@ -283,10 +283,11 @@ int TCombat::GetDefendDamage(void){
}
int TCombat::GetArmorStrength(void){
- // TODO(fusion): We're iterating over inventory slots. Make it clearer?
int Armor = 0;
TCreature *Master = this->Master;
- for(int Position = 1; Position <= 10; Position += 1){
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
Object Obj = GetBodyObject(Master->ID, Position);
if(Obj.exists()){
ObjectType ObjType = Obj.getObjectType();
diff --git a/src/crmain.cc b/src/crmain.cc
index 5d0f2f3..00cd3e2 100644
--- a/src/crmain.cc
+++ b/src/crmain.cc
@@ -226,7 +226,9 @@ TCreature::~TCreature(void){
}
if(this->LoseInventory != 0){ // LOSE_INVENTORY_NONE ?
- for(int Position = 1; Position <= 10; Position += 1){
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
Object Item = GetBodyObject(this->ID, Position);
if(Item == NONE){
continue;
@@ -238,7 +240,7 @@ TCreature::~TCreature(void){
continue;
}
- Move(0, Item, Corpse, -1, false, NONE);
+ ::Move(0, Item, Corpse, -1, false, NONE);
}
}
@@ -487,10 +489,8 @@ int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){
Damage = 0;
}
- // NOTE(fusion): We're iterating over the victim's inventory to apply
- // damage reductions, while the damage is greater than zero.
- for(int Position = 1;
- Position <= 10 && Damage > 0;
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST && Damage > 0;
Position += 1){
Object Obj = GetBodyObject(this->ID, Position);
if(Obj == NONE){
@@ -739,7 +739,9 @@ int TCreature::Damage(TCreature *Attacker, int Damage, int DamageType){
if(Damage == HitPoints){
if(this->Type == PLAYER){
- for(int Position = 1; Position <= 10; Position += 1){
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
Object Obj = GetBodyObject(this->ID, Position);
if(Obj == NONE){
continue;
@@ -2026,11 +2028,11 @@ void ProcessMonsterRaids(void){
}
TCreature *Creature = Spawned[random(0, NumSpawned - 1)];
- Object Bag = GetBodyObject(Creature->ID, 3); // BAG ?
+ Object Bag = GetBodyObject(Creature->ID, INVENTORY_BAG);
if(Bag == NONE){
try{
- Bag = Create(GetBodyContainer(Creature->ID, 3),
- GetSpecialObject(INVENTORY_CONTAINER),
+ Bag = Create(GetBodyContainer(Creature->ID, INVENTORY_BAG),
+ GetSpecialObject(DEFAULT_CONTAINER),
0);
}catch(RESULT r){
error("ProcessMonsterRaids: Exception %d bei Rasse %d"
diff --git a/src/enums.hh b/src/enums.hh
index 2505618..6efcf61 100644
--- a/src/enums.hh
+++ b/src/enums.hh
@@ -221,6 +221,29 @@ enum INSTANCEATTRIBUTE: int {
REMAININGUSES = 17,
};
+// NOTE(fusion): Not in debug symbols.
+enum InventorySlot: int {
+ INVENTORY_ANY = 0,
+ INVENTORY_HEAD = 1,
+ INVENTORY_NECK = 2,
+ INVENTORY_BAG = 3,
+ INVENTORY_TORSO = 4,
+ INVENTORY_RIGHTHAND = 5,
+ INVENTORY_LEFTHAND = 6,
+ INVENTORY_LEGS = 7,
+ INVENTORY_FEET = 8,
+ INVENTORY_FINGER = 9,
+ INVENTORY_AMMO = 10,
+
+ // NOTE(fusion): For iterating over inventory slots.
+ INVENTORY_FIRST = INVENTORY_HEAD,
+ INVENTORY_LAST = INVENTORY_AMMO,
+
+ // NOTE(fusion): For iterating over hand slots.
+ INVENTORY_HAND_FIRST = INVENTORY_RIGHTHAND,
+ INVENTORY_HAND_LAST = INVENTORY_LEFTHAND,
+};
+
enum KNOWNCREATURESTATE: int {
KNOWNCREATURE_FREE = 0,
KNOWNCREATURE_UPTODATE = 1,
@@ -435,16 +458,20 @@ enum Skill: int {
SKILL_SOUL = 22,
};
+// IMPORTANT(fusion): All `DEFAULT_*` values here were renamed from `INVENTORY_*`
+// to avoid collisions with `InventorySlot`. I could also have chosen other prefix
+// for inventory slots but I feel we'd be wasting them here where their meaning
+// were also not clear.
enum SPECIALMEANING: int {
MONEY_ONE = 1,
MONEY_HUNDRED = 2,
MONEY_TENTHOUSAND = 3,
- INVENTORY_RIGHTHAND = 10,
- INVENTORY_LEFTHAND = 11,
- INVENTORY_BODY_MALE = 12,
- INVENTORY_BODY_FEMALE = 13,
- INVENTORY_CONTAINER = 14,
- INVENTORY_FOOD = 15,
+ DEFAULT_RIGHTHAND = 10,
+ DEFAULT_LEFTHAND = 11,
+ DEFAULT_BODY_MALE = 12,
+ DEFAULT_BODY_FEMALE = 13,
+ DEFAULT_CONTAINER = 14,
+ DEFAULT_FOOD = 15,
DEPOT_LOCKER = 20,
DEPOT_CHEST = 21,
PARCEL_NEW = 22,
diff --git a/src/magic.cc b/src/magic.cc
index 1caf83b..a222b3a 100644
--- a/src/magic.cc
+++ b/src/magic.cc
@@ -2409,7 +2409,9 @@ void CancelInvisibility(TCreature *Actor, Object Target, int ManaPoints, int Sou
// it on pvp enforced worlds.
// TODO(fusion): This is probably an inlined function.
if(WorldType == PVP_ENFORCED){
- for(int Position = 1; Position <= 10; Position += 1){
+ for(int Position = INVENTORY_FIRST;
+ Position <= INVENTORY_LAST;
+ Position += 1){
Object Item = GetBodyObject(Victim->ID, Position);
if(Item == NONE){
continue;
@@ -2516,8 +2518,8 @@ void ChangeData(TCreature *Actor, const char *Param){
return;
}
- Object RightHand = GetBodyObject(Actor->ID, 5); // RIGHTHAND ?
- Object LeftHand = GetBodyObject(Actor->ID, 6); // LEFTHAND ?
+ Object RightHand = GetBodyObject(Actor->ID, INVENTORY_RIGHTHAND);
+ Object LeftHand = GetBodyObject(Actor->ID, INVENTORY_LEFTHAND);
if(RightHand != NONE && LeftHand != NONE){
SendMessage(Actor->Connection, TALK_FAILURE_MESSAGE, "First drop one object.");
return;
@@ -2578,9 +2580,9 @@ void EnchantObject(TCreature *Actor, int ManaPoints, int SoulPoints, ObjectType
throw ERROR;
}
- Object Obj = GetBodyObject(Actor->ID, 5); // RIGHTHAND ?
+ Object Obj = GetBodyObject(Actor->ID, INVENTORY_RIGHTHAND);
if(Obj == NONE || Obj.getObjectType() != OldType){
- Obj = GetBodyObject(Actor->ID, 6); // LEFTHAND ?
+ Obj = GetBodyObject(Actor->ID, INVENTORY_LEFTHAND);
if(Obj == NONE || Obj.getObjectType() != OldType){
throw MAGICITEM;
}
@@ -3670,7 +3672,7 @@ static void RuneSpell(uint32 CreatureID, int SpellNr){
bool RuneCreated = false;
ObjectType BlankType = GetSpecialObject(RUNE_BLANK);
- Object RightHand = GetBodyObject(Actor->ID, 5); // RIGHTHAND
+ Object RightHand = GetBodyObject(Actor->ID, INVENTORY_RIGHTHAND);
if(RightHand.exists() && RightHand.getObjectType() == BlankType){
CheckMana(Actor, ManaPoints, SoulPoints, 1000);
ObjectType RuneType = GetNewObjectType(RuneGr, RuneNr);
@@ -3678,7 +3680,7 @@ static void RuneSpell(uint32 CreatureID, int SpellNr){
RuneCreated = true;
}
- Object LeftHand = GetBodyObject(Actor->ID, 6); // LEFTHAND
+ Object LeftHand = GetBodyObject(Actor->ID, INVENTORY_LEFTHAND);
if(LeftHand.exists() && LeftHand.getObjectType() == BlankType){
// TODO(fusion): Ughh... I'm not sure why we're trying to cast the spell
// twice but we need to make so errors from the second cast are only
diff --git a/src/objects.hh b/src/objects.hh
index 39fa8aa..48dbedf 100644
--- a/src/objects.hh
+++ b/src/objects.hh
@@ -8,7 +8,7 @@ enum : int{
TYPEID_MAP_CONTAINER = 0,
TYPEID_HEAD_CONTAINER = 1,
TYPEID_NECK_CONTAINER = 2,
- TYPEID_BACKPACK_CONTAINER = 3,
+ TYPEID_BAG_CONTAINER = 3,
TYPEID_TORSO_CONTAINER = 4,
TYPEID_RIGHTHAND_CONTAINER = 5,
TYPEID_LEFTHAND_CONTAINER = 6,
@@ -29,14 +29,14 @@ struct ObjectType {
const char *getName(int Count);
const char *getDescription(void);
- bool isMapContainer(void) const {
+ bool isMapContainer(void){
return this->TypeID == TYPEID_MAP_CONTAINER;
}
- bool isBodyContainer(void) const {
+ bool isBodyContainer(void){
return this->TypeID == TYPEID_HEAD_CONTAINER
|| this->TypeID == TYPEID_NECK_CONTAINER
- || this->TypeID == TYPEID_BACKPACK_CONTAINER
+ || this->TypeID == TYPEID_BAG_CONTAINER
|| this->TypeID == TYPEID_TORSO_CONTAINER
|| this->TypeID == TYPEID_RIGHTHAND_CONTAINER
|| this->TypeID == TYPEID_LEFTHAND_CONTAINER
@@ -46,10 +46,22 @@ struct ObjectType {
|| this->TypeID == TYPEID_AMMO_CONTAINER;
}
- bool isCreatureContainer(void) const {
+ bool isCreatureContainer(void){
return this->TypeID == TYPEID_CREATURE_CONTAINER;
}
+ bool isTwoHanded(void){
+ return this->getFlag(CLOTHES)
+ && this->getAttribute(BODYPOSITION) == 0;
+ }
+
+ bool isWeapon(void){
+ return this->getFlag(WEAPON)
+ || this->getFlag(BOW)
+ || this->getFlag(THROW)
+ || this->getFlag(WAND);
+ }
+
bool operator==(const ObjectType &Other) const {
return this->TypeID == Other.TypeID;
}
diff --git a/src/operate.cc b/src/operate.cc
new file mode 100644
index 0000000..ef1ba01
--- /dev/null
+++ b/src/operate.cc
@@ -0,0 +1,82 @@
+#include "operate.hh"
+
+#include "stubs.hh"
+
+// TODO(fusion): This could have been a simple return value.
+void CheckContainerDestination(Object Obj, Object Con){
+ Object Help = Con;
+ while(Help != NONE && !Help.getObjectType().isMapContainer()){
+ if(Help == Obj){
+ throw CROSSREFERENCE;
+ }
+ Help = Help.getContainer();
+ }
+
+ ObjectType ConType = Con.getObjectType();
+ if(!ConType.getFlag(CHEST)){
+ int ConObjects = CountObjectsInContainer(Con);
+ int ConCapacity = (int)ConType.getAttribute(CAPACITY);
+ if(ConObjects >= ConCapacity){
+ throw CONTAINERFULL;
+ }
+ }
+}
+
+void CheckInventoryDestination(Object Obj, Object Con, bool Split){
+ ObjectType ObjType = Obj.getObjectType();
+ int ConPosition = GetObjectBodyPosition(Con);
+ bool HandContainer = ConPosition == INVENTORY_RIGHTHAND
+ || ConPosition == INVENTORY_LEFTHAND;
+
+ if(!HandContainer && ConPosition != INVENTORY_AMMO){
+ if(!ObjType.getFlag(CLOTHES)){
+ throw WRONGPOSITION;
+ }
+
+ int ObjPosition = (int)ObjType.getAttribute(BODYPOSITION);
+ if(ObjPosition == 0){
+ throw WRONGPOSITION2;
+ }else if(ObjPosition != ConPosition){
+ throw WRONGCLOTHES;
+ }
+ }
+
+ uint32 CreatureID = GetObjectCreatureID(Con);
+ if(ObjType.isTwoHanded() && HandContainer){
+ Object RightHand = GetBodyObject(CreatureID, INVENTORY_RIGHTHAND);
+ if(RightHand != NONE && RightHand != Obj){
+ throw HANDSNOTFREE;
+ }
+
+ Object LeftHand = GetBodyObject(CreatureID, INVENTORY_LEFTHAND);
+ if(LeftHand != NONE && LeftHand != Obj){
+ throw HANDSNOTFREE;
+ }
+
+ return;
+ }
+
+ if(GetBodyObject(CreatureID, ConPosition) != NONE){
+ throw NOROOM;
+ }
+
+ if(HandContainer){
+ for(int Position = INVENTORY_HAND_FIRST;
+ Position <= INVENTORY_HAND_LAST;
+ Position += 1){
+ Object Other = GetBodyObject(CreatureID, Position);
+ if(Other != NONE){
+ ObjectType OtherType = Other.getObjectType();
+ if(OtherType.isTwoHanded()){
+ throw HANDBLOCKED;
+ }
+
+ if(Split || Other != Obj){
+ if(OtherType.isWeapon() && ObjType.isWeapon()){
+ throw ONEWEAPONONLY;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/operate.hh b/src/operate.hh
new file mode 100644
index 0000000..6270ec7
--- /dev/null
+++ b/src/operate.hh
@@ -0,0 +1,10 @@
+#ifndef TIBIA_OPERATE_HH_
+#define TIBIA_OPERATE_HH_ 1
+
+#include "common.hh"
+#include "map.hh"
+
+void CheckContainerDestination(Object Obj, Object Con);
+void CheckInventoryDestination(Object Obj, Object Con, bool Split);
+
+#endif //TIBIA_OPERATE_HH_
diff --git a/src/stubs.hh b/src/stubs.hh
index f2f1f0b..867d813 100644
--- a/src/stubs.hh
+++ b/src/stubs.hh
@@ -27,6 +27,7 @@ extern void CleanHouseField(int x, int y, int z);
extern void ConvinceMonster(TCreature *Master, TCreature *Slave);
extern void ChallengeMonster(TCreature *Challenger, TCreature *Monster);
extern int CountInventoryObjects(uint32 CreatureID, ObjectType Type, uint32 Value);
+extern int CountObjectsInContainer(Object Con);
extern Object Create(Object Con, ObjectType Type, uint32 Value);
extern Object CreateAtCreature(uint32 CreatureID, ObjectType Type, uint32 Value);
extern TCreature *CreateMonster(int Race, int x, int y, int z, int Home, uint32 Master, bool ShowEffect);
@@ -36,12 +37,14 @@ extern void Delete(Object Obj, int Count);
extern bool FieldPossible(int x, int y, int z, int FieldType);
extern Object GetBodyObject(uint32 CreatureID, int Position);
extern Object GetBodyContainer(uint32 CreatureID, int Position);
+extern int GetObjectBodyPosition(Object Obj);
extern void GetExitPosition(uint16 HouseID, int *x, int *y, int *z);
extern TConnection *GetFirstConnection(void);
extern int GetHeight(int x, int y, int z);
extern TConnection *GetNextConnection(void);
extern const char *GetName(Object Obj);
extern Object GetObject(uint32 CreatureID, int x, int y, int z, int RNum, ObjectType Type);
+extern uint32 GetObjectCreatureID(Object Obj);
extern TPlayer *GetPlayer(uint32 CreatureID);
extern void GraphicalEffect(int x, int y, int z, int Type);
extern void GraphicalEffect(Object Obj, int Type);
@@ -55,6 +58,7 @@ extern void LoadMonsterRaid(const char *FileName, int Start,
extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3);
extern void LogoutAllPlayers(void);
extern void Missile(Object Start, Object Dest, int Type);
+extern void Merge(uint32 CreatureID, Object Obj, Object Dest, int Count, Object Ignore);
extern void Move(uint32 CreatureID, Object Obj, Object Con, int Count, bool NoMerge, Object Ignore);
extern void NetLoadCheck(void);
extern void NetLoadSummary(void);