private void Cmd_fireWeapon() { /* Direction and accuracy */ aimDirection = (fireLocation.position - playerCenter.position); aimDirection.Normalize(); float inaccuracy = (!isAiming) ? playerWeapons.hipAimAngle() : playerWeapons.adsAimAngle(); float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) + (Mathf.Deg2Rad * inaccuracy); //Debug.Log("aim angle: " + aimAngle); aimDirection.x = Mathf.Cos(aimAngle); aimDirection.y = Mathf.Sin(aimAngle); aimDirection.Normalize(); Quaternion bulletRotation = Quaternion.AngleAxis( (aimAngle * Mathf.Rad2Deg) + aimAngleOffset, Vector3.forward); /* Bullet prefab */ GameObject bullet = Instantiate( bulletPrefab, fireLocation.position, bulletRotation); bullet.GetComponent <Rigidbody2D>().velocity = bullet.transform.up * bulletSpeed; /* Spawn the bullet on the Clients */ NetworkServer.Spawn(bullet); Destroy(bullet, bulletLifetime); /* Audio */ if (!isServer) { Cmd_PlayWeaponAudio(); } else { Rpc_PlayWeaponAudio(); } /* Raycast */ RaycastHit2D[] hits = new RaycastHit2D[maxHits]; int numHits = Physics2D.RaycastNonAlloc( fireLocation.position, aimDirection, hits, maxShootDistance, allShootableLayers.value); /* Detect precision damage. Keep track of names of precision objects * hit and the parent object. Hard-coded based on player heirarchy: * Player_vX (contains the life script and principal collider) * -->PointerBody * -->PlayerCenterCriticalSpot */ HashSet <int> needsPrecisionDamage = new HashSet <int>(); foreach (RaycastHit2D hit in hits) { if (hit.collider != null) { int root_id = hit.transform.root.GetInstanceID(); if (hit.collider.tag == precisionTagName) { needsPrecisionDamage.Add(root_id); } Debug.Log("Hit: " + hit.collider.name + " (" + hit.collider.GetInstanceID() + "). Root: " + hit.transform.root.name + " (" + hit.transform.root.GetInstanceID() + "). Tag: " + hit.collider.tag); } } /* Variable to prevent damage through walls. * Relies on the order of the raycast results array. */ bool wasWallHit = false; foreach (RaycastHit2D hit in hits) { if (hit.collider != null) { int root_id = hit.transform.root.GetInstanceID(); bool isPrecisionDamage = needsPrecisionDamage.Contains(root_id); /* Apply damage only to players, for now */ PlayerLife opponentLife = hit.transform.GetComponent <PlayerLife>(); if (opponentLife != null && !wasWallHit) { float damage = 0.0f; float minRange, maxRange; /* Determine range falloff damage based on if ADS or not */ if (!isAiming) { minRange = playerWeapons.EquippedWeapon.hipMinRange; maxRange = playerWeapons.EquippedWeapon.hipMaxRange; } else { minRange = playerWeapons.EquippedWeapon.adsMinRange; maxRange = playerWeapons.EquippedWeapon.adsMaxRange; } /* Calculate the amount of damage to do based on range and precsion hits */ float distance = (this.transform.position - hit.transform.position).magnitude; if (distance <= minRange) { damage = (isPrecisionDamage) ? playerWeapons.EquippedWeapon.roundMaxPrecisionDamage : playerWeapons.EquippedWeapon.roundMaxDamage; } else if (distance >= maxRange) { damage = (isPrecisionDamage) ? playerWeapons.EquippedWeapon.roundMinPrecisionDamage : playerWeapons.EquippedWeapon.roundMinDamage; } else { float lerp = Mathf.Clamp( (distance - minRange) / (maxRange - minRange), 0.0f, 1.0f); float damMax = (isPrecisionDamage) ? playerWeapons.EquippedWeapon.roundMaxPrecisionDamage : playerWeapons.EquippedWeapon.roundMaxDamage; float damMin = (isPrecisionDamage) ? playerWeapons.EquippedWeapon.roundMinPrecisionDamage : playerWeapons.EquippedWeapon.roundMinDamage; damage = Mathf.Lerp(damMax, damMin, lerp); } Debug.Log("Hit Player: " + hit.collider.name); /* Apply Damage */ opponentLife.Cmd_applyDamage(damage, isPrecisionDamage); } /* Hit non-player */ else { wasWallHit = true; } } } /* Editor debug raycast */ Debug.DrawRay( fireLocation.position, aimDirection * maxShootDistance, Color.magenta, 0.1f); /* Update weapon TODO: recoil */ Rpc_UpdatePlayerWeapons(1); } /* Cmd_fireWeapon */