void Player::FireWeapon() { SPADES_MARK_FUNCTION(); Vector3 muzzle = GetEye(); muzzle += GetFront() * 0.01f; // for hit-test debugging std::map<int, HitTestDebugger::PlayerHit> playerHits; std::vector<Vector3> bulletVectors; //Vector3 right = GetRight(); //Vector3 up = GetUp(); int pellets = weapon->GetPelletSize(); float spread = weapon->GetSpread(); GameMap *map = world->GetMap(); // pyspades takes destroying more than one block as a // speed hack (shotgun does this) bool blockDestroyed = false; Vector3 dir2 = GetFront(); for(int i =0 ; i < pellets; i++){ // AoS 0.75's way (dir2 shouldn't be normalized!) dir2.x += (GetRandom() - GetRandom()) * spread; dir2.y += (GetRandom() - GetRandom()) * spread; dir2.z += (GetRandom() - GetRandom()) * spread; Vector3 dir = dir2.Normalize(); bulletVectors.push_back(dir); // first do map raycast GameMap::RayCastResult mapResult; mapResult = map->CastRay2(muzzle, dir, 500); Player *hitPlayer = NULL; float hitPlayerDistance = 0.f; HitBodyPart hitPart = HitBodyPart::None; for(int i = 0; i < world->GetNumPlayerSlots(); i++){ Player *other = world->GetPlayer(i); if(other == this || other == NULL) continue; if(other == this || !other->IsAlive() || other->GetTeamId() >= 2) continue; // quickly reject players unlikely to be hit if(!other->RayCastApprox(muzzle, dir)) continue; HitBoxes hb = other->GetHitBoxes(); Vector3 hitPos; if(hb.head.RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ hitPlayer = other; hitPlayerDistance = dist; hitPart = HitBodyPart::Head; } } if(hb.torso.RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ hitPlayer = other; hitPlayerDistance = dist; hitPart = HitBodyPart::Torso; } } for(int j = 0; j < 3 ;j++){ if(hb.limbs[j].RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ hitPlayer = other; hitPlayerDistance = dist; switch(j) { case 0: hitPart = HitBodyPart::Limb1; break; case 1: hitPart = HitBodyPart::Limb2; break; case 2: hitPart = HitBodyPart::Arms; break; } } } } } Vector3 finalHitPos; finalHitPos = muzzle + dir * 128.f; if(hitPlayer == nullptr && !mapResult.hit) { // might hit water surface. } if(mapResult.hit && (mapResult.hitPos - muzzle).GetLength() < 128.f && (hitPlayer == NULL || (mapResult.hitPos - muzzle).GetLength() < hitPlayerDistance)){ IntVector3 outBlockCoord = mapResult.hitBlock; // TODO: set correct ray distance // FIXME: why ray casting twice? finalHitPos = mapResult.hitPos; if(outBlockCoord.x >= 0 && outBlockCoord.y >= 0 && outBlockCoord.z >= 0 && outBlockCoord.x < map->Width() && outBlockCoord.y < map->Height() && outBlockCoord.z < map->Depth()){ if(outBlockCoord.z == 63) { if(world->GetListener()) world->GetListener()->BulletHitBlock(mapResult.hitPos, mapResult.hitBlock, mapResult.normal); }else if(outBlockCoord.z == 62) { // blocks at this level cannot be damaged if(world->GetListener()) world->GetListener()->BulletHitBlock(mapResult.hitPos, mapResult.hitBlock, mapResult.normal); }else{ int x = outBlockCoord.x; int y = outBlockCoord.y; int z = outBlockCoord.z; SPAssert(map->IsSolid(x, y, z)); Vector3 blockF = {x + .5f, y + .5f, z + .5f}; float distance = (blockF - muzzle).GetLength(); uint32_t color = map->GetColor(x, y, z); int health = color >> 24; health -= weapon->GetDamage(HitTypeBlock, distance); if(health <= 0 && !blockDestroyed){ health = 0; blockDestroyed = true; //send destroy cmd if(world->GetListener() && world->GetLocalPlayer() == this) world->GetListener()->LocalPlayerBlockAction (outBlockCoord, BlockActionTool); } color = (color & 0xffffff) | ((uint32_t)health << 24); if(map->IsSolid(x, y, z)) map->Set(x, y, z, true, color); if(world->GetListener()) world->GetListener()->BulletHitBlock(mapResult.hitPos, mapResult.hitBlock, mapResult.normal); } } }else if(hitPlayer != NULL){
void Player::FireWeapon() { SPADES_MARK_FUNCTION(); Vector3 muzzle = GetEye(); muzzle += GetFront() * 0.01f;/* muzzle += GetRight() * 0.4f; muzzle -= GetUp() * 0.3f;*/ Vector3 right = GetRight(); Vector3 up = GetUp(); int pellets = weapon->GetPelletSize(); float spread = weapon->GetSpread(); GameMap *map = world->GetMap(); // pyspades takes destroying more than one block as a // speed hack (shotgun does this) bool blockDestroyed = false; Vector3 dir2 = GetFront(); for(int i =0 ; i < pellets; i++){ // AoS 0.75's way (dir2 shouldn't be normalized!) dir2.x += (GetRandom() * 2.f - 1.f) * spread; dir2.y += (GetRandom() * 2.f - 1.f) * spread; dir2.z += (GetRandom() * 2.f - 1.f) * spread; Vector3 dir = dir2.Normalize(); // first do map raycast GameMap::RayCastResult mapResult; mapResult = map->CastRay2(muzzle, dir, 500); Player *hitPlayer = NULL; float hitPlayerDistance = 0.f; int hitFlag = 0; for(int i = 0; i < world->GetNumPlayerSlots(); i++){ Player *other = world->GetPlayer(i); if(other == this || other == NULL) continue; if(other == this || !other->IsAlive() || other->GetTeamId() >= 2) continue; if(!other->RayCastApprox(muzzle, dir)) continue; HitBoxes hb = other->GetHitBoxes(); Vector3 hitPos; if(hb.head.RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ if(hitPlayer != other){ hitPlayer = other; hitFlag = 0; } hitPlayerDistance = dist; hitFlag = 1; // head } } if(hb.torso.RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ if(hitPlayer != other){ hitPlayer = other; hitFlag = 0; } hitPlayerDistance = dist; hitFlag = 2; // torso } } for(int j = 0; j < 3 ;j++){ if(hb.limbs[j].RayCast(muzzle, dir, &hitPos)) { float dist = (hitPos - muzzle).GetLength(); if(hitPlayer == NULL || dist < hitPlayerDistance){ if(hitPlayer != other){ hitPlayer = other; hitFlag = 0; } hitPlayerDistance = dist; if(j == 2) hitFlag = 8; // arms else hitFlag = 4; // leg } } } } Vector3 finalHitPos; finalHitPos = muzzle + dir * 128.f; if(mapResult.hit && (mapResult.hitPos - muzzle).GetLength() < 128.f && (hitPlayer == NULL || (mapResult.hitPos - muzzle).GetLength() < hitPlayerDistance)){ IntVector3 outBlockCoord = mapResult.hitBlock; // TODO: set correct ray distance // FIXME: why ray casting twice? finalHitPos = mapResult.hitPos; if(outBlockCoord.x >= 0 && outBlockCoord.y >= 0 && outBlockCoord.z >= 0 && outBlockCoord.x < map->Width() && outBlockCoord.y < map->Height() && outBlockCoord.z < map->Depth()){ if(outBlockCoord.z < 62){ int x = outBlockCoord.x; int y = outBlockCoord.y; int z = outBlockCoord.z; SPAssert(map->IsSolid(x, y, z)); Vector3 blockF = {x + .5f, y + .5f, z + .5f}; float distance = (blockF - muzzle).GetLength(); uint32_t color = map->GetColor(x, y, z); int health = color >> 24; health -= weapon->GetDamage(HitTypeBlock, distance); if(health <= 0 && !blockDestroyed){ health = 0; blockDestroyed = true; //send destroy cmd if(world->GetListener() && world->GetLocalPlayer() == this) world->GetListener()->LocalPlayerBlockAction (outBlockCoord, BlockActionTool); } color = (color & 0xffffff) | ((uint32_t)health << 24); if(map->IsSolid(x, y, z)) map->Set(x, y, z, true, color); if(world->GetListener()) world->GetListener()->BulletHitBlock(mapResult.hitPos, mapResult.hitBlock, mapResult.normal); } } }else if(hitPlayer != NULL){