/* Copyright (c) 2013 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #include "GunCasing.h" #include "IRenderer.h" #include "GameMap.h" #include "Client.h" #include "World.h" #include "IAudioDevice.h" #include #include "ParticleSpriteEntity.h" #include "IAudioChunk.h" namespace spades { namespace client{ GunCasing::GunCasing(Client *client, IModel *model, IAudioChunk *dropSound, IAudioChunk *waterSound, Vector3 pos, Vector3 dir, Vector3 flyDir): client(client), renderer(client->GetRenderer()), model(model), dropSound(dropSound), waterSound(waterSound){ if(dropSound) dropSound->AddRef(); if(waterSound) waterSound->AddRef(); Vector3 up = MakeVector3(0, 0, 1); Vector3 right = Vector3::Cross(dir, up).Normalize(); up = Vector3::Cross(right, dir); matrix = Matrix4::FromAxis(right, dir, up, pos); up = MakeVector3(0, 0, -1); rotAxis=Vector3::Cross(up, flyDir.Normalize()); groundTime = 0.f; onGround = false; vel = flyDir * 10.f; rotSpeed = 40.f; } GunCasing::~GunCasing(){ if(dropSound) dropSound->Release(); if(waterSound) waterSound->Release(); } static Vector3 RandomAxis(){ Vector3 v; v.x = GetRandom() - GetRandom(); v.y = GetRandom() - GetRandom(); v.z = GetRandom() - GetRandom(); return v.Normalize(); } bool GunCasing::Update(float dt) { if(onGround){ groundTime += dt; if(groundTime > 2.f){ return false; } GameMap *map = client->GetWorld()->GetMap(); if(!map->ClipWorld(groundPos.x, groundPos.y, groundPos.z)){ return false; } }else{ Matrix4 lastMat = matrix; matrix = matrix * Matrix4::Rotate(rotAxis, dt * rotSpeed); matrix = Matrix4::Translate(vel * dt) * matrix; vel.z += dt * 32.f; IntVector3 lp = matrix.GetOrigin().Floor(); GameMap *m = client->GetWorld()->GetMap(); if(lp.z >= 63){ // dropped into water float dist = (client->GetLastSceneDef().viewOrigin - matrix.GetOrigin()).GetPoweredLength(); if(waterSound){ if(dist < 40.f * 40.f && !client->IsMuted()){ IAudioDevice *dev = client->GetAudioDevice(); AudioParam param; param.referenceDistance = .6f; param.pitch = .9f + GetRandom() * .2f; dev->Play(waterSound, lastMat.GetOrigin(), param); } waterSound = NULL; } if(dist < 40.f * 40.f){ int splats = rand() % 3; Handle img = client->GetRenderer()->RegisterImage("Gfx/White.tga"); Vector4 col = {1, 1, 1, 0.8f}; Vector3 pt = matrix.GetOrigin(); pt.z = 62.99f; for(int i = 0; i < splats; i++){ ParticleSpriteEntity *ent = new ParticleSpriteEntity(client, img, col); ent->SetTrajectory(pt, MakeVector3(GetRandom()-GetRandom(), GetRandom()-GetRandom(), -GetRandom()) * 2.f, 1.f, .4f); ent->SetRotation(GetRandom() * (float)M_PI * 2.f); ent->SetRadius(0.1f + GetRandom()*GetRandom()*0.1f); ent->SetLifeTime(2.f, 0.f, 1.f); client->AddLocalEntity(ent); } } return false; } if(m->ClipWorld(lp.x,lp.y,lp.z)){ // hit a wall IntVector3 lp2 = lastMat.GetOrigin().Floor(); if (lp.z != lp2.z && ((lp.x == lp2.x && lp.y == lp2.y) || !m->ClipWorld(lp.x, lp.y, lp2.z))){ vel.z = -vel.z; if(lp2.z < lp.z){ // ground hit if(vel.GetLength() < .5f + dt * 100.f && !dropSound){ // stick to ground onGround = true; groundPos = lp; // move to surface float z = matrix.GetOrigin().z; float shift = z - floorf(z); matrix = Matrix4::Translate(0,0,-shift) * matrix; // lie Vector3 v1 = matrix.GetAxis(0); Vector3 v2 = matrix.GetAxis(1); v1.z = 0; v2.z = 0; v1 = v1.Normalize(); v2 = v2.Normalize(); Vector3 v3 = Vector3::Cross(v1, v2).Normalize(); v1 = Vector3::Cross(v2, v3).Normalize(); matrix = Matrix4::FromAxis(v1, v2, v3, matrix.GetOrigin()); }else{ if(dropSound){ float dist = (client->GetLastSceneDef().viewOrigin - matrix.GetOrigin()).GetPoweredLength(); if(dist < 40.f * 40.f && !client->IsMuted()){ IAudioDevice *dev = client->GetAudioDevice(); AudioParam param; param.referenceDistance = .6f; dev->Play(dropSound, lastMat.GetOrigin(), param); } dropSound = NULL; } } } }else if(lp.x != lp2.x && ((lp.y == lp2.y && lp.z == lp2.z) || !m->ClipWorld(lp2.x, lp.y, lp.z))) vel.x = -vel.x; else if(lp.y != lp2.y && ((lp.x == lp2.x && lp.z == lp2.z) || !m->ClipWorld(lp.x, lp2.y, lp.z))) vel.y = -vel.y; else return false; if(!onGround){ matrix = lastMat; vel *= .2f; rotAxis = RandomAxis(); Vector3 r; r.x = GetRandom() - GetRandom(); r.y = GetRandom() - GetRandom(); r.z = GetRandom() - GetRandom(); vel += r * 0.1f; rotSpeed *= .2f; } } } return true; } void GunCasing::Render3D() { ModelRenderParam param; param.matrix = matrix * Matrix4::Scale(.02f); if(groundTime > 1.f){ // sink float move = (groundTime - 1.f) * .2f; param.matrix = Matrix4::Translate(0,0,move) * param.matrix; } renderer->RenderModel(model, param); } } }