Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleplayer Crossbow Bolts #21

Closed
ampreeT opened this issue Aug 13, 2020 · 2 comments · Fixed by #141
Closed

Singleplayer Crossbow Bolts #21

ampreeT opened this issue Aug 13, 2020 · 2 comments · Fixed by #141
Labels
entity-issues engine entity-related bugs/crashes/issues

Comments

@ampreeT
Copy link
Owner

ampreeT commented Aug 13, 2020

mp causes crossbow to be automatic. also sets unzoomed bolts to be explosive and zoomed bolts to be normal.

possible solution:

  • hook bolt spawn and set bolt to zoomed type
  • hook weapon think and modify firerate
@ampreeT ampreeT added enhancement New feature or request entity-issues engine entity-related bugs/crashes/issues and removed enhancement New feature or request labels Aug 13, 2020
@ampreeT
Copy link
Owner Author

ampreeT commented Aug 30, 2024

From what I've seen, we may need to hook a few functions. The following functions are candidates for hooking as they call pGameRules->IsMultiplayer().

CBlackMesaBaseWeaponIronSights::ToggleIronSights
CWeapon_Crossbow::FireBolt
CWeapon_Crossbow::Deploy
CWeapon_Crossbow::GetPrimaryAttackActivity
CWeapon_Crossbow::PrimaryAttack
CWeapon_Crossbow::ItemPostFrame
CWeapon_Crossbow::WeaponIdle

@ampreeT
Copy link
Owner Author

ampreeT commented Sep 4, 2024

CWeapon_Crossbow::FireBolt spawns either entity factory tracerbullet or grenade_bolt depending on whether if it is singleplayer or multiplayer. I believe the first argument of the virtual function CWeapon_Crossbow::FireBolt sets explosive bolts.

CWeapon_Crossbow::PrimaryAttack calls into CWeapon_Crossbow::FireBolt or CWeapon_Crossbow::FireHitscanBolt depending on if it is multiplayer and/or in ironsights.

Linux Disassembly (note that the variables towards the bottom of the function are wrongly named due to being reused):

void __thiscall CWeapon_Crossbow::PrimaryAttack(CWeapon_Crossbow *this)

{
  ushort uVar1;
  ushort uVar2;
  ushort uVar3;
  uint *puVar4;
  bool bVar5;
  ushort *puVar6;
  bool bIsMultiplayer;
  undefined bExplosive;
  CBlackMesaPlayer *pPlayer;
  int iAttackMode;
  undefined4 iAttackActivity;
  char bIsMultiplayer1;
  int iVar7;
  int *piVar8;
  ushort *puVar9;
  ushort *puVar10;
  undefined4 uVar11;
  longdouble lVar12;
  longdouble lVar13;
  float fVar14;
  bool bIsValidEntity;
  code *pfnSequenceDuration;
  
  pPlayer = (CBlackMesaPlayer *)CBlackMesaBaseWeapon::GetBlackMesaPlayer(this);
  if (pPlayer == (CBlackMesaPlayer *)0x0) {
    return;
  }
  iAttackMode = (*(code *)this->vftable->GetCurrentAttackMode)(this);
  if (iAttackMode == 0) {
    pfnSequenceDuration = (code *)this->vftable->FireBolt;
    bExplosive = (**(code **)(*g_pGameRules + 0x8c))(g_pGameRules);
    bIsValidEntity = (bool)(*pfnSequenceDuration)(this,bExplosive);
  }
  else if (iAttackMode == 1) {
    bIsMultiplayer1 = (**(code **)(*g_pGameRules + 0x8c))(g_pGameRules);
    if (bIsMultiplayer1 == '\0') {
      bIsValidEntity = (bool)(*(code *)this->vftable->FireBolt)(this,0);
    }
    else {
      bIsValidEntity = (bool)(*(code *)this->vftable->FireHitscanBolt)(this);
    }
  }
  else {
    bIsValidEntity = false;
  }
  uVar11 = 1;
  if (*(int *)&this->field_0x4e8 < 1) {
    uVar11 = 0xb;
  }
  iAttackActivity = (*(code *)this->vftable->GetPrimaryAttackActivity)(this);
  bIsMultiplayer = (bool)(**(code **)(*g_pGameRules + 0x8c))(g_pGameRules);
  if ((bIsMultiplayer == false) &&
     (bVar5 = 0 < *(int *)&this->field_0x4e8, bVar5 != this->m_bNeedsToRearm)) {
    if (this->field_0x5c == '\0') {
      puVar4 = *(uint **)&this->field_0x20;
      if ((puVar4 != (uint *)0x0) && ((*puVar4 & 0x100) == 0)) {
        *puVar4 = *puVar4 | 1;
        puVar9 = (ushort *)CBaseEdict::GetChangeAccessor(puVar4);
        puVar6 = g_pSharedChangeInfo;
        if (puVar9[1] == *g_pSharedChangeInfo) {
          uVar2 = *puVar9;
          uVar3 = g_pSharedChangeInfo[(uint)uVar2 * 0x14 + 0x14];
          if (uVar3 == 0) {
LAB_003299fc:
            g_pSharedChangeInfo[(uint)uVar2 * 0x14 + 0x14] = uVar3 + 1;
            puVar6[(uint)uVar2 * 0x14 + uVar3 + 1] = 0x5bc;
          }
          else if (g_pSharedChangeInfo[(uint)uVar2 * 0x14 + 1] != 0x5bc) {
            puVar10 = g_pSharedChangeInfo + (uint)uVar2 * 0x14 + 2;
            do {
              if (puVar10 == g_pSharedChangeInfo + (uint)uVar2 * 0x14 + 2 + (uVar3 - 1 & 0xffff)) {
                if (uVar3 != 0x13) goto LAB_003299fc;
                goto LAB_00329a23;
              }
              uVar1 = *puVar10;
              puVar10 = puVar10 + 1;
            } while (uVar1 != 0x5bc);
          }
        }
        else if (g_pSharedChangeInfo[0x7d1] == 100) {
LAB_00329a23:
          puVar9[1] = 0;
          *puVar4 = *puVar4 | 0x100;
        }
        else {
          *puVar9 = g_pSharedChangeInfo[0x7d1];
          puVar6 = g_pSharedChangeInfo;
          g_pSharedChangeInfo[0x7d1] = g_pSharedChangeInfo[0x7d1] + 1;
          puVar9[1] = *puVar6;
          puVar6 = g_pSharedChangeInfo;
          uVar2 = *puVar9;
          g_pSharedChangeInfo[(uint)uVar2 * 0x14 + 1] = 0x5bc;
          puVar6[(uint)uVar2 * 0x14 + 0x14] = 1;
        }
      }
    }
    else {
      this->field_0x60 = this->field_0x60 | 1;
    }
    this->m_bNeedsToRearm = bVar5;
  }
  if (bIsValidEntity == false) goto failed_to_create_bolt_or_normal;
  (*(code *)this->vftable->WeaponSound_1)(this,uVar11,0);
  (*(code *)this->vftable->SendWeaponAnim)(this,iAttackActivity);
  lVar12 = (longdouble)(*(code *)this->vftable->GetFireRate)(this,iAttackMode);
  uVar11 = *(undefined4 *)&this->m_nSequence;
  pfnSequenceDuration = (code *)this->vftable->SequenceDuration;
  if (this->field_0x345 == '\0') {
    piVar8 = *(int **)&this->field_0x488;
    if (piVar8 == (int *)0x0) {
      iVar7 = CBaseEntity::GetModel(this);
      if (iVar7 == 0) {
        piVar8 = *(int **)&this->field_0x488;
      }
      else {
        CBaseAnimating::LockStudioHdr(this);
        piVar8 = *(int **)&this->field_0x488;
      }
      if (piVar8 == (int *)0x0) goto LAB_0032982e;
    }
    if (*piVar8 == 0) goto LAB_0032982e;
  }
  else {
LAB_0032982e:
    piVar8 = (int *)0x0;
  }
  lVar13 = (longdouble)(*pfnSequenceDuration)(this,piVar8,uVar11);
  if ((float)lVar12 <= (float)lVar13) {
    uVar11 = *(undefined4 *)&this->m_nSequence;
    pfnSequenceDuration = (code *)this->vftable->SequenceDuration;
    if (this->field_0x345 == '\0') {
      piVar8 = *(int **)&this->field_0x488;
      if (piVar8 == (int *)0x0) {
        iVar7 = CBaseEntity::GetModel(this);
        if (iVar7 == 0) {
          piVar8 = *(int **)&this->field_0x488;
        }
        else {
          CBaseAnimating::LockStudioHdr(this);
          piVar8 = *(int **)&this->field_0x488;
        }
        if (piVar8 == (int *)0x0) goto LAB_003298b7;
      }
      if (*piVar8 == 0) goto LAB_003298b7;
    }
    else {
LAB_003298b7:
      piVar8 = (int *)0x0;
    }
    lVar12 = (longdouble)(*pfnSequenceDuration)(this,piVar8,uVar11);
  }
  else {
    lVar12 = (longdouble)(*(code *)this->vftable->GetFireRate)(this,iAttackMode);
  }
  fVar14 = *(float *)(gpGlobals + 0xc);
  if (iAttackMode == 0) {
    CBlackMesaBaseWeapon::SetNextPrimaryAttack
              ((CBlackMesaBaseWeapon *)this,(float)lVar12 + fVar14,false);
    fVar14 = *(float *)(gpGlobals + 0xc);
  }
  else if (iAttackMode == 1) {
    CBlackMesaBaseWeapon::SetNextSecondaryAttack
              ((CBlackMesaBaseWeapon *)this,(float)lVar12 + fVar14,false);
    fVar14 = *(float *)(gpGlobals + 0xc);
  }
  (*(code *)this->vftable->SetWeaponIdleTime)(this,fVar14);
                    /* 0x3f800000 == float(1.0) (can't retype) */
  (*(code *)this->vftable->AddViewKick_1)(this,iAttackMode,0x3f800000);
  CBaseAnimating::DoMuzzleFlash(this);
failed_to_create_bolt_or_normal:
  bIsMultiplayer1 = (**(code **)(*g_pGameRules + 0x8c))(g_pGameRules);
  if (((bIsMultiplayer1 != '\0') &&
      (bIsMultiplayer1 = (*(code *)this->vftable->InIronsights)(this), bIsMultiplayer1 != '\0')) &&
     (iAttackMode = (*(code *)this->vftable->Clip1)(this), 0 < iAttackMode)) {
    CBlackMesaBaseWeaponIronSights::ToggleIronSights(this);
  }
  (*(code *)pPlayer->vftable->OnMyWeaponFired)(pPlayer,this);
  return;
}

@ampreeT ampreeT changed the title crossbow sp bolts Singleplayer Crossbow Bolts Sep 4, 2024
@ampreeT ampreeT linked a pull request Sep 11, 2024 that will close this issue
ampreeT added a commit that referenced this issue Sep 12, 2024
SP Ironsights #20, Tau Prediction Fix, SP Crossbow Bolts #21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
entity-issues engine entity-related bugs/crashes/issues
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant