From cd8f3f412c2a5f49ee25aab83079a655b88d6def Mon Sep 17 00:00:00 2001 From: fusion32 Date: Mon, 9 Jun 2025 01:19:20 -0300 Subject: finish `cract.cc` --- TODO.md | 5 +- reference/game.c | 710 ------------------------------------------------------- src/cr.hh | 9 + src/cract.cc | 401 ++++++++++++++++++++++++++++++- src/magic.cc | 2 +- src/stubs.hh | 10 + 6 files changed, 419 insertions(+), 718 deletions(-) diff --git a/TODO.md b/TODO.md index 50b2e97..32dcda7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,8 @@ ## TODO NEXT -- CRACT.CC -- CRPLAYER.CC, CRNONPL.CC -- HOUSES.CC - INFO.CC - OPERATE.CC +- HOUSES.CC +- CRPLAYER.CC, CRNONPL.CC - MOVEUSE.CC - SENDING.CC - RECEIVING.CC diff --git a/reference/game.c b/reference/game.c index 7e6d16d..d430658 100644 --- a/reference/game.c +++ b/reference/game.c @@ -43083,716 +43083,6 @@ void __static_initialization_and_destruction_0(int __initialize_p,int __priority return; } -// DWARF original prototype: void Trade(TCreature * this, Object * Obj, ulong PartnerID) - -void __thiscall TCreature::Trade(TCreature *this,Object *Obj,ulong PartnerID) - -{ - Object OVar1; - bool bVar2; - TPlayer *pTVar3; - int iVar4; - undefined4 *puVar5; - TPlayer *Partner; - TConnection *Connection; - char *Name; - Object local_5c; - Object Help_1; - Object local_4c [4]; - Object local_3c [4]; - Object local_2c; - Object Help; - - if (this->Type == PLAYER) { - // try { // try from 080c0226 to 080c05fa has its CatchHandler @ 080c0612 - bVar2 = Object::exists(Obj); - if (bVar2) { - local_2c = (Object)Obj->ObjectID; - bVar2 = ObjectAccessible(this->ID,&local_2c,1); - if (bVar2) { - if (this->ID == PartnerID) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 9; - } - else { - pTVar3 = GetPlayer(PartnerID); - if ((pTVar3 == (TPlayer *)0x0) || ((pTVar3->super_TCreature).Type != PLAYER)) { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x1b; - } - else { - local_2c.ObjectID = ::NONE.ObjectID; - local_3c[0].ObjectID = ::NONE.ObjectID; - if (this[1].super_TSkillBase.TimerList[0xb] == (TSkill *)::NONE.ObjectID) { - local_3c[0] = (Object)Obj->ObjectID; - iVar4 = CountObjects(local_3c); - if (iVar4 < 0x65) { - local_3c[0].ObjectID = (pTVar3->super_TCreature).CrObject.ObjectID; - local_2c.ObjectID = (this->CrObject).ObjectID; - iVar4 = ObjectDistance(&local_2c,local_3c); - if (iVar4 < 3) { - bVar2 = ThrowPossible(this->posx,this->posy,this->posz, - (pTVar3->super_TCreature).posx, - (pTVar3->super_TCreature).posy, - (pTVar3->super_TCreature).posz,0); - if (bVar2) { - bVar2 = false; - local_3c[0].ObjectID = ::NONE.ObjectID; - local_2c.ObjectID = ::NONE.ObjectID; - if (((pTVar3->TradeObject).ObjectID != ::NONE.ObjectID) && - (pTVar3->TradePartner != this->ID)) { - bVar2 = true; - } - if (!bVar2) { - OVar1.ObjectID = (pTVar3->TradeObject).ObjectID; - if (OVar1.ObjectID != ::NONE.ObjectID) { - while (local_2c.ObjectID = OVar1.ObjectID, - Object::getObjectType(local_4c), - local_4c[0].ObjectID != 0) { - local_4c[0] = (Object)Obj->ObjectID; - if (local_2c.ObjectID == local_4c[0].ObjectID) - goto LAB_080c03c7; - Object::getContainer(&local_5c); - local_4c[0].ObjectID = local_5c.ObjectID; - local_2c.ObjectID = local_5c.ObjectID; - OVar1.ObjectID = local_2c.ObjectID; - } - local_5c = (Object)Obj->ObjectID; - while (Object::getObjectType(local_4c), - local_4c[0].ObjectID != 0) { - local_4c[0].ObjectID = local_5c.ObjectID; - if ((pTVar3->TradeObject).ObjectID == - local_5c.ObjectID) goto LAB_080c03c7; - Object::getContainer(&local_2c); - local_4c[0].ObjectID = local_2c.ObjectID; - local_5c.ObjectID = local_2c.ObjectID; - } - } - local_4c[0] = (Object)Obj->ObjectID; - this[1].super_TSkillBase.TimerList[0xb] = - (TSkill *)local_4c[0].ObjectID; - this[1].super_TSkillBase.TimerList[0xc] = - (TSkill *)PartnerID; - *(undefined1 *) - (this[1].super_TSkillBase.TimerList + 0xd) = 0; - local_5c.ObjectID = ::NONE.ObjectID; - bVar2 = (pTVar3->TradeObject).ObjectID != - ::NONE.ObjectID; - if (bVar2) { - SendTradeOffer((pTVar3->super_TCreature).Connection, - this->Name,false,local_4c); - local_4c[0].ObjectID = - (ulong)this[1].super_TSkillBase.TimerList[0xb]; - SendTradeOffer(this->Connection,this->Name,true, - local_4c); - local_4c[0].ObjectID = - (pTVar3->TradeObject).ObjectID; - Name = (pTVar3->super_TCreature).Name; - Connection = this->Connection; - } - else { - Name = this->Name; - SendMessage((pTVar3->super_TCreature).Connection, - 0x16,"%s wants to trade with you.",Name) - ; - local_4c[0].ObjectID = - (ulong)this[1].super_TSkillBase.TimerList[0xb]; - Connection = this->Connection; - } - SendTradeOffer(Connection,Name,!bVar2,local_4c); - return; - } - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x36; - } - else { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 7; - } - } - else { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 5; - } - } - else { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x37; - } - } - else { - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0x35; - } - } - } - goto LAB_080c03d9; - } - } -LAB_080c03c7: - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 1; - } - else { - error(&DAT_08106e40); - puVar5 = (undefined4 *)__cxa_allocate_exception(4); - *puVar5 = 0xffffffff; - } -LAB_080c03d9: - // WARNING: Subroutine does not return - __cxa_throw(puVar5,&RESULT::typeinfo,0); -} - - - -// DWARF original prototype: void Use(TCreature * this, Object * Obj1, Object * Obj2, uchar Dummy) - -void __thiscall TCreature::Use(TCreature *this,Object *Obj1,Object *Obj2,uchar Dummy) - -{ - int iVar1; - bool bVar2; - undefined4 *puVar3; - ulong uVar4; - int *piVar5; - int Delay; - bool bVar6; - int iStack_70; - uchar Dummy_local; - int local_6c; - int local_68; - int x; - int y; - int z; - Object local_4c [4]; - ulong local_3c; - Object local_2c [7]; - - // try { // try from 080c0652 to 080c098c has its CatchHandler @ 080c09a0 - bVar2 = Object::exists(Obj1); - if (bVar2) { - bVar2 = false; - local_2c[0].ObjectID = ::NONE.ObjectID; - local_3c = ::NONE.ObjectID; - if ((Obj2->ObjectID != ::NONE.ObjectID) && (bVar6 = Object::exists(Obj2), !bVar6)) { - bVar2 = true; - } - if (!bVar2) { - bVar2 = false; - local_3c = ::NONE.ObjectID; - local_2c[0].ObjectID = ::NONE.ObjectID; - local_4c[0] = (Object)Obj2->ObjectID; - if (local_4c[0].ObjectID != ::NONE.ObjectID) { - Object::getObjectType(local_2c); - bVar6 = ObjectType::getFlag((ObjectType *)local_2c,DISTUSE); - if (!bVar6) { - local_2c[0] = (Object)Obj2->ObjectID; - bVar6 = ObjectInRange(this->ID,local_2c,1); - if (!bVar6) { - bVar2 = true; - } - } - local_4c[0] = (Object)Obj2->ObjectID; - } - if (bVar2) { - GetObjectCoordinates(local_4c,&local_68,&x,&y); - bVar2 = ToDoClear(this); - if ((bVar2) && (this->Type == PLAYER)) { - SendSnapback(this->Connection); - } - local_4c[0] = (Object)Obj1->ObjectID; - uVar4 = GetObjectCreatureID(local_4c); - if (uVar4 != this->ID) { - z = Obj1->ObjectID; - ToDoMove(this,(Object *)&z,0xffff,0,0,'\x01'); - } - ToDoGo(this,local_68,x,y,false,0x7fffffff); - z = Obj2->ObjectID; - local_4c[0] = (Object)Obj1->ObjectID; - ToDoUse(this,'\x02',local_4c,(Object *)&z); - if (this->NrToDo != 0) { - this->LockToDo = true; - this->ActToDo = 0; - local_6c = CalculateDelay(this); - iStack_70 = 1; - piVar5 = &stack0xffffff90; - if (0 < local_6c) { - piVar5 = &local_6c; - } - iVar1 = *piVar5; - priority_queue<>::insert(&ToDoQueue); - this->NextWakeup = iVar1 + ServerMilliseconds; - } - return; - } - bVar2 = false; - z = ::NONE.ObjectID; - bVar6 = local_4c[0].ObjectID != ::NONE.ObjectID; - local_4c[0].ObjectID = ::NONE.ObjectID; - if (bVar6) { - Object::getObjectType(local_4c); - bVar6 = ObjectType::getFlag((ObjectType *)local_4c,DISTUSE); - if (bVar6) { - local_4c[0] = (Object)Obj2->ObjectID; - bVar6 = ObjectInRange(this->ID,local_4c,7); - if (!bVar6) { - bVar2 = true; - } - } - } - if (!bVar2) { - z = Obj2->ObjectID; - if (z != ::NONE.ObjectID) { - this->EarliestMultiuseTime = ServerMilliseconds + 1000; - z = Obj2->ObjectID; - } - local_4c[0] = (Object)Obj1->ObjectID; - ::Use(this->ID,local_4c,(Object *)&z,Dummy); - return; - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 5; - goto LAB_080c0733; - } - } - puVar3 = (undefined4 *)__cxa_allocate_exception(4); - *puVar3 = 0x1f; -LAB_080c0733: - // WARNING: Subroutine does not return - __cxa_throw(puVar3,&RESULT::typeinfo,0); -} - - - -// DWARF original prototype: void Turn(TCreature * this, Object * Obj) - -void __thiscall TCreature::Turn(TCreature *this,Object *Obj) - -{ - bool bVar1; - undefined4 *puVar2; - Object local_1c [5]; - - // try { // try from 080c09cf to 080c0a25 has its CatchHandler @ 080c0a26 - bVar1 = Object::exists(Obj); - if (bVar1) { - local_1c[0] = (Object)Obj->ObjectID; - ::Turn(this->ID,local_1c); - return; - } - puVar2 = (undefined4 *)__cxa_allocate_exception(4); - *puVar2 = 0x1f; - // WARNING: Subroutine does not return - __cxa_throw(puVar2,&RESULT::typeinfo,0); -} - -// DWARF original prototype: void NotifyGo(TCreature * this) - -void __thiscall TCreature::NotifyGo(TCreature *this) - -{ - int iVar1; - int iVar2; - TConnection *Connection; - bool bVar3; - bool bVar4; - bool bVar5; - int Waypoints; - int iVar6; - CreatureType CVar7; - ulong uVar8; - int iVar9; - TPlayer *pTVar10; - int dx; - uint uVar11; - TCreature *Window; - int dy; - uint uVar12; - TCreature *Obj_00; - TCreature *Con_00; - TPlayer *Partner; - bool DiagonalMove; - int dz; - int local_68; - int x; - int y; - int z; - Object Obj; - Object local_4c [4]; - Object local_3c [4]; - Object local_2c; - Object Con; - - local_2c.ObjectID = (this->CrObject).ObjectID; - GetObjectCoordinates(&local_2c,&local_68,&x,&y); - iVar9 = this->posx; - uVar11 = local_68 - iVar9; - iVar1 = this->posy; - uVar12 = x - iVar1; - iVar2 = this->posz; - bVar3 = false; - iVar6 = y - iVar2; - if (((iVar6 == 0) && (uVar11 != 0)) && (uVar12 != 0)) { - bVar3 = true; - } - MoveChainCreature(this,local_68,x); - CVar7 = this->Type; - if ((CVar7 == PLAYER) && (Connection = this->Connection, Connection != (TConnection *)0x0)) { - if (((iVar6 + 1U < 3) && (uVar11 + 1 < 3)) && (uVar12 + 1 < 3)) { - if (iVar6 == 1) { - this->posz = this->posz + 1; - this->posx = this->posx + -1; - this->posy = this->posy + -1; - SendFloors(Connection,false); - uVar11 = uVar11 + 1; - uVar12 = uVar12 + 1; - } - if (iVar6 == -1) { - this->posz = this->posz + -1; - uVar11 = uVar11 - 1; - this->posx = this->posx + 1; - uVar12 = uVar12 - 1; - this->posy = this->posy + 1; - SendFloors(this->Connection,true); - } - for (; 0 < (int)uVar11; uVar11 = uVar11 - 1) { - this->posx = this->posx + 1; - SendRow(this->Connection,1); - } - for (; (int)uVar11 < 0; uVar11 = uVar11 + 1) { - this->posx = this->posx + -1; - SendRow(this->Connection,3); - } - for (; 0 < (int)uVar12; uVar12 = uVar12 - 1) { - this->posy = this->posy + 1; - SendRow(this->Connection,2); - } - for (; (int)uVar12 < 0; uVar12 = uVar12 + 1) { - this->posy = this->posy + -1; - SendRow(this->Connection,0); - } - } - else { - this->posx = this->posx + uVar11; - this->posy = this->posy + uVar12; - this->posz = this->posz + iVar6; - SendFullScreen(Connection); - } - CVar7 = this->Type; - } - else { - this->posx = this->posx + uVar11; - this->posy = this->posy + uVar12; - this->posz = this->posz + iVar6; - } - if (CVar7 != PLAYER) goto LAB_080c0c2d; - Window = (TCreature *)0x0; - do { - Obj_00 = this; - Con_00 = Window; - TPlayer::GetOpenContainer((TPlayer *)&local_2c,(int)this); - local_3c[0].ObjectID = ::NONE.ObjectID; - if (local_2c.ObjectID != ::NONE.ObjectID) { - bVar4 = Object::exists(&local_2c); - if (bVar4) { - Con_00 = (TCreature *)0x1; - Obj_00 = (TCreature *)local_3c; - local_3c[0].ObjectID = local_2c.ObjectID; - bVar4 = ObjectAccessible(this->ID,(Object *)Obj_00,1); - if (bVar4) goto LAB_080c0c07; - local_4c[0] = (Object)::NONE.ObjectID; - Con_00 = (TCreature *)local_4c; - } - else { - error("TCreature::NotifyGo: OpenContainer existiert nicht. (%s, [%d,%d,%d]->[%d,%d,%d])\n" - ,this->Name,iVar9,iVar1,iVar2,this->posx,this->posy,this->posz); - local_3c[0].ObjectID = ::NONE.ObjectID; - Con_00 = (TCreature *)local_3c; - } - TPlayer::SetOpenContainer((TPlayer *)this,(int)Window,(Object *)Con_00); - Obj_00 = Window; - SendCloseContainer(this->Connection,(int)Window); - } -LAB_080c0c07: - Window = (TCreature *)((int)&Window->_vptr_TCreature + 1); - } while ((int)Window < 0x10); - local_4c[0].ObjectID = ::NONE.ObjectID; - local_3c[0].ObjectID = ::NONE.ObjectID; - if (this[1].super_TSkillBase.TimerList[0xb] == (TSkill *)::NONE.ObjectID) goto LAB_080c0c2d; - pTVar10 = GetPlayer((ulong)this[1].super_TSkillBase.TimerList[0xc]); - bVar4 = Object::exists((Object *)(this[1].super_TSkillBase.TimerList + 0xb)); - if (!bVar4) { - error("TCreature::NotifyGo: Handelsobjekt existiert nicht mehr.\n",Obj_00,Con_00); - error(&DAT_081072f3,this->Name,this->posx,this->posy,this->posz); - if (pTVar10 != (TPlayer *)0x0) { - error("# Partner %s an [%d,%d,%d]\n",(pTVar10->super_TCreature).Name, - (pTVar10->super_TCreature).posx,(pTVar10->super_TCreature).posy, - (pTVar10->super_TCreature).posz); - } - } - bVar4 = false; - if (pTVar10 == (TPlayer *)0x0) { -LAB_080c0dba: - bVar4 = true; - } - else { - local_3c[0].ObjectID = (ulong)this[1].super_TSkillBase.TimerList[0xb]; - bVar5 = ObjectAccessible(this->ID,local_3c,1); - if (!bVar5) goto LAB_080c0dba; - local_2c.ObjectID = (pTVar10->super_TCreature).CrObject.ObjectID; - z = (this->CrObject).ObjectID; - iVar9 = ObjectDistance((Object *)&z,&local_2c); - if ((2 < iVar9) || - (bVar5 = ThrowPossible(this->posx,this->posy,this->posz,(pTVar10->super_TCreature).posx, - (pTVar10->super_TCreature).posy,(pTVar10->super_TCreature).posz,0) - , !bVar5)) goto LAB_080c0dba; - } - if (bVar4) { - SendCloseTrade(this->Connection); - SendMessage(this->Connection,0x17,"Trade cancelled."); - TPlayer::RejectTrade((TPlayer *)this); - } -LAB_080c0c2d: - GetFirstObject(); - while( true ) { - Object::getObjectType(local_4c); - bVar4 = ObjectType::getFlag((ObjectType *)local_4c,BANK); - if (bVar4) break; - local_4c[0].ObjectID = ::NONE.ObjectID; - local_3c[0].ObjectID = ::NONE.ObjectID; - if (z == ::NONE.ObjectID) break; - Object::getNextObject(local_3c); - z = local_3c[0].ObjectID; - local_4c[0].ObjectID = z; - } - local_4c[0] = (Object)::NONE.ObjectID; - if (z == ::NONE.ObjectID) { - error("TCreature::NotifyGo: Auf Feld [%d,%d,%d] befindet sich kein Bank.\n",this->posx, - this->posy,this->posz); - } - else { - Object::getObjectType(local_4c); - uVar8 = ObjectType::getAttribute((ObjectType *)local_4c,WAYPOINTS); - if (bVar3) { - uVar8 = uVar8 * 3; - } - iVar9 = GetSpeed(this); - this->EarliestWalkTime = - ((Beat + -1 + (int)(uVar8 * 1000) / iVar9) / Beat) * Beat + ServerMilliseconds; - } - return; -} - - - -// DWARF original prototype: void NotifyTurn(TCreature * this, Object * DestCon) - -void __thiscall TCreature::NotifyTurn(TCreature *this,Object *DestCon) - -{ - int dy; - int iVar1; - int local_28; - int x; - int y; - int z; - - z = DestCon->ObjectID; - GetObjectCoordinates((Object *)&z,&local_28,&x,&y); - local_28 = local_28 - this->posx; - iVar1 = x - this->posy; - if (local_28 < 1) { - if (local_28 < 0) { - this->Direction = 3; - } - else if (iVar1 < 0) { - this->Direction = 0; - } - else if (x != this->posy && -1 < iVar1) { - this->Direction = 2; - } - } - else { - this->Direction = 1; - } - return; -} - - - -// DWARF original prototype: void NotifyDelete(TCreature * this) - -void __thiscall TCreature::NotifyDelete(TCreature *this) - -{ - CreatureType CVar1; - - if (this->Type == PLAYER) { - CVar1 = PLAYER; - if (this->Connection != (TConnection *)0x0) { - TConnection::Logout(this->Connection,0x1e,true); - CVar1 = this->Type; - this->LoggingOut = true; - } - if (CVar1 == PLAYER) { - TPlayer::RejectTrade((TPlayer *)this); - } - } - DeleteChainCreature(this); - return; -} - - - -// DWARF original prototype: void NotifyCreate(TCreature * this) - -void __thiscall TCreature::NotifyCreate(TCreature *this) - -{ - InsertChainCreature(this,0,0); - return; -} - - - -// DWARF original prototype: void NotifyChangeInventory(TCreature * this) - -void __thiscall TCreature::NotifyChangeInventory(TCreature *this) - -{ - TSkill *pTVar1; - anon_union_4_2_730cd3ca_for_TOutfit_2 aVar2; - bool bVar3; - bool bVar4; - int i; - int iVar5; - int iVar6; - ulong uVar7; - ulong uVar8; - bool SkillsChanged; - int i_1; - int b; - ulong uVar9; - int SkillNr; - ulong local_ac; - int iStack_9c; - int NewDelta [25]; - ulong local_2c; - Object Obj; - - local_2c = ::NONE.ObjectID; - if ((this->CrObject).ObjectID != ::NONE.ObjectID) { - bVar3 = Object::exists(&this->CrObject); - if (bVar3) { - TCombat::CheckCombatValues(&this->Combat); - if (this->Type == PLAYER) { - iVar5 = 0; - do { - NewDelta[iVar5 + -1] = 0; - iVar5 = iVar5 + 1; - } while (iVar5 < 0x19); - uVar9 = 1; - do { - bVar3 = false; - GetBodyObject((ulong)&local_2c,this->ID); - local_ac = ::NONE.ObjectID; - if (local_2c == ::NONE.ObjectID) { -LAB_080c12f2: - bVar3 = true; - } - else { - Object::getObjectType((Object *)&SkillNr); - bVar4 = ObjectType::getFlag((ObjectType *)&SkillNr,SKILLBOOST); - if (!bVar4) goto LAB_080c12f2; - } - if (!bVar3) { - Object::getObjectType((Object *)&SkillNr); - bVar3 = ObjectType::getFlag((ObjectType *)&SkillNr,CLOTHES); - if (bVar3) { - Object::getObjectType((Object *)&SkillNr); - uVar7 = ObjectType::getAttribute((ObjectType *)&SkillNr,BODYPOSITION); - if (uVar7 == uVar9) { - Object::getObjectType((Object *)&SkillNr); - uVar7 = ObjectType::getAttribute((ObjectType *)&SkillNr,SKILLNUMBER) - ; - if ((int)uVar7 < 0x19) { - Object::getObjectType((Object *)&SkillNr); - uVar8 = ObjectType::getAttribute - ((ObjectType *)&SkillNr,SKILLMODIFICATION); - if (0x7fff < (int)uVar8) { - uVar8 = uVar8 - 0x10000; - } - NewDelta[uVar7 - 1] = NewDelta[uVar7 - 1] + uVar8; - } - else { - Object::getObjectType((Object *)&SkillNr); - error(&DAT_08107080,SkillNr,uVar7); - } - } - } - else { - Object::getObjectType((Object *)&SkillNr); - error("TCreature::NotifyChangeInventory: Objekt %d hat SKILLBOOST, aber nicht CLOTHES.\n" - ,SkillNr); - } - } - uVar9 = uVar9 + 1; - } while ((int)uVar9 < 0xb); - bVar3 = false; - iVar5 = 0; - do { - pTVar1 = (this->super_TSkillBase).Skills[iVar5]; - if (pTVar1->DAct != NewDelta[iVar5 + -1]) { - pTVar1->DAct = NewDelta[iVar5 + -1]; - if (iVar5 == 4) { - AnnounceChangedCreature(this->ID,4); - } - if (iVar5 == 0x10) { - if (NewDelta[0xf] < 1) { - pTVar1 = (this->super_TSkillBase).Skills[0x10]; - iVar6 = (*pTVar1->_vptr_TSkill[0xd])(pTVar1); - if (iVar6 == 0) { - aVar2 = (this->OrgOutfit).field_1; - (this->Outfit).OutfitID = (this->OrgOutfit).OutfitID; - (this->Outfit).field_1 = aVar2; - } - } - else { - bVar3 = false; - if (((this->Outfit).OutfitID == 0) && - ((this->Outfit).field_1.ObjectType == 0)) { - bVar3 = true; - } - if ((!bVar3) && - (pTVar1 = (this->super_TSkillBase).Skills[0x10], - iVar6 = (*pTVar1->_vptr_TSkill[0xd])(pTVar1), iVar6 != 0)) { - TSkillBase::SetTimer(&this->super_TSkillBase); - } - (this->Outfit).OutfitID = 0; - (this->Outfit).field_1.ObjectType = 0; - } - AnnounceChangedCreature(this->ID,3); - } - bVar3 = true; - } - iVar5 = iVar5 + 1; - } while (iVar5 < 0x19); - if (bVar3) { - SendPlayerSkills(this->Connection); - TPlayer::CheckState((TPlayer *)this); - } - SendPlayerData(this->Connection); - } - } - else { - error("TCreature::NotifyChangeInventory: Kreatur-Objekt existiert nicht.\n"); - error("# Kreatur: %s, Position: %d/%d/%d.\n",this->Name,this->posx,this->posy,this->posz - ); - } - } - return; -} - // WARNING: Unknown calling convention -- yet parameter storage is locked void _GLOBAL__I__ZN9TShortwayC2EP9TCreatureii(void) diff --git a/src/cr.hh b/src/cr.hh index 2c60163..5ad1676 100644 --- a/src/cr.hh +++ b/src/cr.hh @@ -526,6 +526,11 @@ struct TCreature: TSkillBase { void ToDoAttack(void); void ToDoTalk(int Mode, const char *Addressee, const char *Text, bool CheckSpamming); void ToDoChangeState(int NewState); + void NotifyGo(void); + void NotifyTurn(Object DestCon); + void NotifyCreate(void); + void NotifyDelete(void); + void NotifyChangeInventory(void); // VIRTUAL FUNCTIONS // ================= @@ -679,6 +684,7 @@ struct TMonster: TNonplayer { struct TPlayer: TCreature { //TPlayer(void); + // TODO(fusion): Eventually sort these out when we implement player functions. uint8 GetRealProfession(void); uint8 GetEffectiveProfession(void); uint8 GetActiveProfession(void); @@ -701,6 +707,9 @@ struct TPlayer: TCreature { void ClearPlayerkillingMarks(void); void SaveInventory(void); + Object GetOpenContainer(int ContainerNr); + void SetOpenContainer(int ContainerNr, Object Con); + void RejectTrade(void); // VIRTUAL FUNCTIONS // ================= diff --git a/src/cract.cc b/src/cract.cc index 8306ad5..3d56aa2 100644 --- a/src/cract.cc +++ b/src/cract.cc @@ -1,4 +1,5 @@ #include "cr.hh" +#include "config.hh" #include "operate.hh" #include "stubs.hh" @@ -364,8 +365,8 @@ void TCreature::Go(int DestX, int DestY, int DestZ){ } if(!this->MovePossible(DestX, DestY, DestZ, true, false)){ - bool Diagonal = (OrigX != DestX && OrigY != DestY); - if(this->Type == PLAYER && !Diagonal){ + bool DiagonalMove = (OrigX != DestX && OrigY != DestY); + if(this->Type == PLAYER && !DiagonalMove){ // TODO(fusion): These are quite similar to `MagicClimbing`. Perhaps // there is an inlined function here that checks whether climbing is // possible. @@ -454,7 +455,7 @@ void TCreature::Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){ try{ Object Con = GetBodyContainer(this->ID, Position); CheckInventoryDestination(Obj, Con, false); - }catch(...){ + }catch(RESULT r){ continue; } @@ -585,7 +586,7 @@ void TCreature::Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){ this->ToDoMove(Obj, 0xFFFF, 0, 0, 1); } - // NOTE(fusion): Walk to destination then try again. + // NOTE(fusion): Walk to destination and try again. this->ToDoGo(DestX, DestY, DestZ, false, INT_MAX); this->ToDoMove(Obj, DestX, DestY, DestZ, 1); this->ToDoStart(); @@ -595,6 +596,132 @@ void TCreature::Move(Object Obj, int DestX, int DestY, int DestZ, uint8 Count){ } } +void TCreature::Trade(Object Obj, uint32 PartnerID){ + if(this->Type != PLAYER){ + error("TCreature::Trade: Nur Spieler können handeln.\n"); + throw ERROR; + } + + if(!Obj.exists() || !ObjectAccessible(this->ID, Obj, 1)){ + throw NOTACCESSIBLE; + } + + if(this->ID == PartnerID){ + throw CROSSREFERENCE; + } + + TPlayer *Partner = GetPlayer(PartnerID); + if(Partner == NULL || Partner->Type != PLAYER){ + throw PLAYERNOTONLINE; + } + + if(((TPlayer*)this)->TradeObject != NONE){ + throw ALREADYTRADING; + } + + if(CountObjects(Obj) > 100){ + throw TOOMANYOBJECTS; + } + + if(ObjectDistance(this->CrObject, Partner->CrObject) > 2){ + throw OUTOFRANGE; + } + + if(!ThrowPossible(this->posx, this->posy, this->posz, + Partner->posx, Partner->posy, Partner->posz, 0)){ + throw CANNOTTHROW; + } + + if(Partner->TradeObject != NONE && Partner->TradePartner != this->ID){ + throw PARTNERTRADING; + } + + // NOTE(fusion): Check if one object is contained by the other. + if(Partner->TradeObject != NONE){ + Object Help = Partner->TradeObject; + while(Help != NONE && !Help.getObjectType().isMapContainer()){ + if(Help == Obj){ + throw NOTACCESSIBLE; + } + Help = Help.getContainer(); + } + + Help = Obj; + while(Help != NONE && !Help.getObjectType().isMapContainer()){ + if(Help == Partner->TradeObject){ + throw NOTACCESSIBLE; + } + Help = Help.getContainer(); + } + } + + ((TPlayer*)this)->TradeObject = Obj; + ((TPlayer*)this)->TradePartner = PartnerID; + ((TPlayer*)this)->TradeAccepted = false; + + if(Partner->TradeObject != NONE){ + SendTradeOffer(Partner->Connection, this->Name, false, Obj); + SendTradeOffer(this->Connection, this->Name, true, Obj); + SendTradeOffer(this->Connection, Partner->Name, false, Partner->TradeObject); + }else{ + SendMessage(Partner->Connection, TALK_INFO_MESSAGE, + "%s wants to trade with you.", this->Name); + SendTradeOffer(this->Connection, this->Name, true, Obj); + } +} + +void TCreature::Use(Object Obj1, Object Obj2, uint8 Dummy){ + if(!Obj1.exists()){ + throw DESTROYED; + } + + if(Obj2 != NONE){ + if(!Obj2.exists()){ + throw DESTROYED; + } + + bool DistUse = Obj1.getObjectType().getFlag(DISTUSE); + if(!DistUse && !ObjectInRange(this->ID, Obj2, 1)){ + int ObjX2, ObjY2, ObjZ2; + GetObjectCoordinates(Obj2, &ObjX2, &ObjY2, &ObjZ2); + + if(this->ToDoClear() && this->Type == PLAYER){ + SendSnapback(this->Connection); + } + + // NOTE(fusion): Pick up object 1 if it's not in our inventory. + if(GetObjectCreatureID(Obj1) != this->ID){ + this->ToDoMove(Obj1, 0xFFFF, 0, 0, 1); + } + + // NOTE(fusion): Walk to object 2 and try again. + this->ToDoGo(ObjX2, ObjY2, ObjZ2, false, INT_MAX); + // TODO(fusion): Missing `Dummy` here? Probably not because this + // could only be triggered if object 2 exists and Dummy is only + // used when opening containers. + this->ToDoUse(2, Obj1, Obj2); + this->ToDoStart(); + return; + } + + if(DistUse && !ObjectInRange(this->ID, Obj2, 7)){ + throw OUTOFRANGE; + } + + this->EarliestMultiuseTime = ServerMilliseconds + 1000; + } + + ::Use(this->ID, Obj1, Obj2, Dummy); +} + +void TCreature::Turn(Object Obj){ + if(!Obj.exists()){ + throw DESTROYED; + } + + ::Turn(this->ID, Obj); +} + void TCreature::Attack(void){ this->Combat.Attack(); } @@ -1192,3 +1319,269 @@ void TCreature::ToDoChangeState(int NewState){ TD.ChangeState.NewState = NewState; this->ToDoAdd(TD); } + +void TCreature::NotifyGo(void){ + // IMPORTANT(fusion): This and the function that does move `this->CrObject` + // should be the only ones where the output of `GetObjectCoordinates` will + // differ from `this->posx`, `this->posy`, and `this->posz`. + + int DestX, DestY, DestZ; + GetObjectCoordinates(this->CrObject, &DestX, &DestY, &DestZ); + MoveChainCreature(this, DestX, DestY); + + int OrigX = this->posx; + int OrigY = this->posy; + int OrigZ = this->posz; + bool DiagonalMove = (DestX != OrigX && DestY != OrigY && DestZ == OrigZ); + + // IMPORTANT(fusion): `SendFloors` and `SendRow` will use the current creature + // position to know where to pull fields from, so we need to keep them updated + // as we go. + if(this->Type == PLAYER && this->Connection != NULL){ + int DistanceX = std::abs(DestX - OrigX); + int DistanceY = std::abs(DestY - OrigY); + int DistanceZ = std::abs(DestZ - OrigZ); + if(DistanceX <= 1 && DistanceY <= 1 && DistanceZ <= 1){ + while(this->posz < DestZ){ + this->posx -= 1; + this->posy -= 1; + this->posz += 1; + SendFloors(this->Connection, false); + } + + while(this->posz > DestZ){ + this->posx += 1; + this->posy += 1; + this->posz -= 1; + SendFloors(this->Connection, true); + } + + while(this->posx < DestX){ + this->posx += 1; + SendRow(this->Connection, DIRECTION_EAST); + } + + while(this->posx > DestX){ + this->posx -= 1; + SendRow(this->Connection, DIRECTION_WEST); + } + + while(this->posy < DestY){ + this->posy += 1; + SendRow(this->Connection, DIRECTION_SOUTH); + } + + while(this->posy > DestY){ + this->posy -= 1; + SendRow(this->Connection, DIRECTION_NORTH); + } + }else{ + this->posx = DestX; + this->posy = DestY; + this->posz = DestZ; + SendFullScreen(this->Connection); + } + }else{ + this->posx = DestX; + this->posy = DestY; + this->posz = DestZ; + } + + if(this->Type == PLAYER){ + // NOTE(fusion): Check open containers. + for(int ContainerNr = 0; + ContainerNr < NARRAY(TPlayer::OpenContainer); + ContainerNr += 1){ + Object Con = ((TPlayer*)this)->GetOpenContainer(ContainerNr); + if(Con == NONE){ + continue; + } + + if(!Con.exists()){ + error("TCreature::NotifyGo: OpenContainer existiert nicht. (%s, [%d,%d,%d]->[%d,%d,%d])\n", + this->Name, OrigX, OrigY, OrigZ, DestX, DestY, DestZ); + continue; + } + + if(!ObjectAccessible(this->ID, Con, 1)){ + ((TPlayer*)this)->SetOpenContainer(ContainerNr, NONE); + SendCloseContainer(this->Connection, ContainerNr); + } + } + + // NOTE(fusion): Check trade. + Object TradeObject = ((TPlayer*)this)->TradeObject; + if(TradeObject != NONE){ + TPlayer *Partner = GetPlayer(((TPlayer*)this)->TradePartner); + if(!TradeObject.exists()){ + error("TCreature::NotifyGo: Handelsobjekt existiert nicht mehr.\n"); + error("# Händler %s an [%d,%d,%d]\n", this->Name, DestX, DestY, DestZ); + if(Partner != NULL){ + error("# Partner %s an [%d,%d,%d]\n", Partner->Name, + Partner->posx, Partner->posy, Partner->posz); + } + } + + if(Partner == NULL || !ObjectAccessible(this->ID, TradeObject, 1) + || ObjectDistance(this->CrObject, Partner->CrObject) > 2 + || !ThrowPossible(this->posx, this->posy, this->posz, + Partner->posx, Partner->posy, Partner->posz, 0)){ + SendCloseTrade(this->Connection); + SendMessage(this->Connection, TALK_FAILURE_MESSAGE, "Trade cancelled."); + ((TPlayer*)this)->RejectTrade(); + } + } + } + + // TODO(fusion): This is probably an inlined function to get the first object + // with some specific flag on a field. + int Waypoints; + Object Bank = GetFirstObject(DestX, DestY, DestZ); + while(Bank != NONE){ + ObjectType BankType = Bank.getObjectType(); + if(BankType.getFlag(BANK)){ + Waypoints = BankType.getAttribute(WAYPOINTS); + break; + } + Bank = Bank.getNextObject(); + } + + if(Bank != NONE){ + // NOTE(fusion): Diagonal movement has three times the delay of a regular one. + if(DiagonalMove){ + Waypoints *= 3; + } + + // NOTE(fusion): Compute the step interval and quantize it to be a multiple + // of `Beat`, rounding up. + int Delay = (Waypoints * 1000) / this->GetSpeed(); + int BeatCount = (Delay + Beat - 1) / Beat; + this->EarliestWalkTime = ServerMilliseconds + BeatCount * Beat; + }else{ + error("TCreature::NotifyGo: Auf Feld [%d,%d,%d] befindet sich kein Bank.\n", + DestX, DestY, DestZ); + } +} + +void TCreature::NotifyTurn(Object DestCon){ + int DestX, DestY, DestZ; + GetObjectCoordinates(DestCon, &DestX, &DestY, &DestZ); + + // NOTE(fusion): This is somewhat similar to `TCreature::Rotate`. + int OffsetX = DestX - this->posx; + int OffsetY = DestY - this->posy; + if(OffsetX > 0){ + this->Direction = DIRECTION_EAST; + }else if(OffsetX < 0){ + this->Direction = DIRECTION_WEST; + }else if(OffsetY < 0){ + this->Direction = DIRECTION_NORTH; + }else if(OffsetY > 0){ + this->Direction = DIRECTION_SOUTH; + } +} + +void TCreature::NotifyCreate(void){ + InsertChainCreature(this, 0, 0); +} + +void TCreature::NotifyDelete(void){ + if(this->Type == PLAYER){ + if(this->Connection != NULL){ + this->Connection->Logout(30, true); + this->LoggingOut = true; + } + + ((TPlayer*)this)->RejectTrade(); + } + + DeleteChainCreature(this); +} + +void TCreature::NotifyChangeInventory(void){ + if(this->CrObject == NONE){ + return; + } + + if(!this->CrObject.exists()){ + error("TCreature::NotifyChangeInventory: Kreatur-Objekt existiert nicht.\n"); + error("# Kreatur: %s, Position: %d/%d/%d.\n", + this->Name, this->posx, this->posy, this->posz); + return; + } + + this->Combat.CheckCombatValues(); + if(this->Type == PLAYER){ + int NewDelta[NARRAY(this->Skills)]; + for(int Position = INVENTORY_FIRST; + Position <= INVENTORY_LAST; + Position += 1){ + Object Obj = GetBodyObject(this->ID, Position); + if(Obj == NONE){ + continue; + } + + ObjectType ObjType = Obj.getObjectType(); + if(!ObjType.getFlag(SKILLBOOST)){ + continue; + } + + if(!ObjType.getFlag(CLOTHES)){ + error("TCreature::NotifyChangeInventory: Objekt %d hat SKILLBOOST, aber nicht CLOTHES.\n", + ObjType.TypeID); + continue; + } + + if((int)ObjType.getAttribute(BODYPOSITION) != Position){ + continue; + } + + int SkillNr = (int)ObjType.getAttribute(SKILLNUMBER); + if(SkillNr < 0 || SkillNr >= NARRAY(this->Skills)){ + error("TCreature::NotifyChangeInventory: Objekt %d hat ungültige SKILLNUMBER %d.\n", + ObjType.TypeID, SkillNr); + continue; + } + + // IMPORTANT(fusion): The value for the skill modification is stored + // as uint32 but is bitcast from a signed integer on load, meaning that + // that bitcasting it back should retrieve the original signed value. + // See `LoadObjects`. + NewDelta[SkillNr] += (int)ObjType.getAttribute(SKILLMODIFICATION); + } + + bool SkillsChanged = false; + for(int SkillNr = 0; + SkillNr < NARRAY(this->Skills); + SkillNr += 1){ + TSkill *Skill = this->Skills[SkillNr]; + if(Skill->DAct != NewDelta[SkillNr]){ + SkillsChanged = true; + Skill->DAct = NewDelta[SkillNr]; + if(SkillNr == SKILL_GO_STRENGTH){ + AnnounceChangedCreature(this->ID, 4); // CREATURE_GO_STRENGTH_CHANGED ? + }else if(SkillNr == SKILL_ILLUSION){ + if(NewDelta[SKILL_ILLUSION] > 0){ + if(this->Outfit.OutfitID != 0 || this->Outfit.ObjectType != 0){ + if(Skill->TimerValue() != 0){ + this->SetTimer(SKILL_ILLUSION, 0, 0, 0, -1); + } + this->Outfit.OutfitID = 0; + this->Outfit.ObjectType = 0; + } + }else if(Skill->TimerValue() == 0){ + this->Outfit = this->OrgOutfit; + } + AnnounceChangedCreature(this->ID, 3); // CREATURE_OUTFIT_CHANGED ? + } + } + } + + if(SkillsChanged){ + SendPlayerSkills(this->Connection); + ((TPlayer*)this)->CheckState(); + } + + SendPlayerData(this->Connection); + } +} diff --git a/src/magic.cc b/src/magic.cc index a222b3a..46da798 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -3690,7 +3690,7 @@ static void RuneSpell(uint32 CreatureID, int SpellNr){ ObjectType RuneType = GetNewObjectType(RuneGr, RuneNr); Change(LeftHand, RuneType, Amount); RuneCreated = true; - }catch(...){ + }catch(RESULT r){ if(!RuneCreated){ throw; } diff --git a/src/stubs.hh b/src/stubs.hh index 867d813..c2ca289 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 CountObjects(Object Obj); extern int CountObjectsInContainer(Object Con); extern Object Create(Object Con, ObjectType Type, uint32 Value); extern Object CreateAtCreature(uint32 CreatureID, ObjectType Type, uint32 Value); @@ -63,6 +64,7 @@ extern void Move(uint32 CreatureID, Object Obj, Object Con, int Count, bool NoMe extern void NetLoadCheck(void); extern void NetLoadSummary(void); extern void NotifyAllCreatures(Object Obj, int Type, Object OldCon); +extern bool ObjectAccessible(uint32 CreatureID, Object Obj, int Range); extern int ObjectDistance(Object Obj1, Object Obj2); extern bool ObjectInRange(uint32 CreatureID, Object Obj, int Range); extern void ProcessCommunicationControl(void); @@ -82,7 +84,12 @@ extern bool SearchSummonField(int *x, int *y, int *z, int Distance); extern void SendAll(void); extern void SendAmbiente(TConnection *Connection); extern void SendClearTarget(TConnection *Connection); +extern void SendCloseContainer(TConnection *Connection, int ContainerNr); +extern void SendCloseTrade(TConnection *Connection); extern void SendEditList(TConnection *Connection, uint8 ListType, uint32 ID, const char *Text); +extern void SendFullScreen(TConnection *Connection); +extern void SendFloors(TConnection *Connection, bool Up); +extern void SendRow(TConnection *Connection, int Direction); extern void SendMails(TPlayerData *PlayerData); extern void SendMarkCreature(TConnection *Connection, uint32 CreatureID, int Color); extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ...) ATTR_PRINTF(3, 4); @@ -91,6 +98,7 @@ extern void SendPlayerSkills(TConnection *Connection); extern void SendPlayerState(TConnection *Connection, uint8 State); extern void SendResult(TConnection *Connection, RESULT r); extern void SendSnapback(TConnection *Connection); +extern void SendTradeOffer(TConnection *Connection, const char *Name, bool OwnOffer, Object Obj); extern void ShowGuestList(uint16 HouseID, TPlayer *Player, char *Buffer); extern void ShowSubownerList(uint16 HouseID, TPlayer *Player, char *Buffer); extern void ShowNameDoor(Object Door, TPlayer *Player, char *Buffer); @@ -98,5 +106,7 @@ extern void Talk(uint32 CreatureID, int Mode, const char *Addressee, const char extern void TextualEffect(Object Obj, int Color, const char *Text, ...) ATTR_PRINTF(3, 4); extern bool ThrowPossible(int FromX, int FromY, int FromZ, int ToX, int ToY, int ToZ, int Power); +extern void Turn(uint32 CreatureID, Object Obj); +extern void Use(uint32 CreatureID, Object Obj1, Object Object2, uint8 Info); #endif //TIBIA_STUBS_HH_ -- cgit v1.2.3