finished the game (i will probably not be touching this again)
This commit is contained in:
+105
-8
@@ -226,7 +226,7 @@ void Engine::Initiate()
|
||||
shoot.knockback = 1.5f;
|
||||
shoot.stun = 0.8f;
|
||||
shoot.sound = "assets/gunshot.mp3";
|
||||
shoot.screenshake = 0.45f; // a punch landing is 0.3, the combo finisher 0.6
|
||||
shoot.screenshake = 0.65f; // a punch landing is 0.3, the combo finisher 0.6
|
||||
if (std::filesystem::exists("assets/rifleexecutionhit.glb")) // whoever it kills goes down like this, filmed by its Camera bone
|
||||
shoot.killAnimation = charactermanager->LoadAnimation("rifleexecutionhit", "assets/rifleexecutionhit.glb", "humanoid");
|
||||
shoot.killSounds = { { 40.0f, "assets/dramaticwoosh.mp3" }, { 60.0f, "assets/glassbreak.mp3" } };
|
||||
@@ -238,7 +238,7 @@ void Engine::Initiate()
|
||||
// 2: a switchblade out of nowhere into whoever's in front. It stays in them a while, bleeding them.
|
||||
Ability stab;
|
||||
stab.name = "Stab";
|
||||
stab.description = "Pull a switchblade and stab whoever is right in front of you. The knife stays in them, making them bleed. Stuns what it hits.";
|
||||
stab.description = "Pull a switchblade and stab whoever is right in front of you. The knife stays in them, making them bleed. Stuns what it hits. Hyper armor: hits don't stop it.";
|
||||
stab.effect = Ability::Effect::Stab;
|
||||
stab.animation = charactermanager->LoadAnimation("riflestab", "assets/riflestab.glb", "humanoid"); // brings the Knife bone
|
||||
stab.lastFrame = 60.0f; // 1 s at 60 fps, played at its own speed
|
||||
@@ -246,6 +246,7 @@ void Engine::Initiate()
|
||||
stab.hitExitFrame = 50.0f; // landing it, it's over 10 frames early
|
||||
stab.cooldown = 3.0f;
|
||||
stab.moveSpeedMultiplier = 0.0f;
|
||||
stab.hyperArmor = true; // trades hits rather than being knocked out of it
|
||||
stab.lungeDistance = 1.5f; // steps in over the first 15 frames
|
||||
stab.lungeStartFrame = 0.0f;
|
||||
stab.lungeEndFrame = 15.0f;
|
||||
@@ -481,7 +482,14 @@ void Engine::Initiate()
|
||||
// The camera shakes by shake when it was the player's.
|
||||
auto LandHit = [&](const Character* attacker, Character& target, float damage, const glm::vec3& shove, float shake,
|
||||
float stun) {
|
||||
// Hyper armor takes the damage and nothing else
|
||||
const bool armored = target.HasHyperArmor();
|
||||
Hurt(target, damage);
|
||||
if (armored && !target.IsDead()) {
|
||||
if (attacker == controlled)
|
||||
camera->AddScreenshake(shake);
|
||||
return;
|
||||
}
|
||||
target.Flinch(); // nothing if that was the hit that put them down
|
||||
target.Stun(stun);
|
||||
if (target.ragdoll) {
|
||||
@@ -968,7 +976,8 @@ void Engine::Initiate()
|
||||
float life = 0.0f; // real seconds left, 0 when it's free
|
||||
};
|
||||
std::vector<Tracer> tracers;
|
||||
const float tracerTime = 0.06f;
|
||||
const float tracerTime = 0.16f; // real seconds, thinning out to nothing over it
|
||||
const float tracerThickness = 0.07f;
|
||||
|
||||
auto ShowTracer = [&](const glm::vec3& from, const glm::vec3& direction, float length) {
|
||||
Tracer* tracer = nullptr;
|
||||
@@ -979,12 +988,12 @@ void Engine::Initiate()
|
||||
tracer = &tracers.back();
|
||||
tracer->mesh = CreateCubeMesh();
|
||||
tracer->mesh->name = "tracer";
|
||||
tracer->mesh->colorOverride = glm::vec4(1.0f, 0.86f, 0.55f, 1.0f);
|
||||
tracer->mesh->colorOverride = glm::vec4(1.0f, 0.82f, 0.45f, 1.0f);
|
||||
tracer->mesh->emissive = 1.0f;
|
||||
tracer->mesh->castshadow = false;
|
||||
}
|
||||
// A thin box from end to end, its length along its own z
|
||||
tracer->mesh->t.ScaleTo({ 0.025f, 0.025f, length });
|
||||
tracer->mesh->t.ScaleTo({ tracerThickness, tracerThickness, length });
|
||||
tracer->mesh->t.RotateToQuaternion(LookAt(direction));
|
||||
tracer->mesh->t.TranslateTo(from + direction * (length * 0.5f));
|
||||
if (tracer->life <= 0.0f)
|
||||
@@ -1004,10 +1013,66 @@ void Engine::Initiate()
|
||||
muzzleSparks->emitangle = glm::vec3(0.7f, 0.7f, 0.0f); // radians of spread
|
||||
muzzleSparks->facecamera = true;
|
||||
muzzleSparks->BindToRenderSystem();
|
||||
const int sparksPerShot = 14;
|
||||
const int sparksPerShot = 32;
|
||||
|
||||
// The flash itself: a few big bright blobs right on the muzzle for a frame or two
|
||||
ParticleEmitter* muzzleFlash = new ParticleEmitter();
|
||||
muzzleFlash->name = "muzzle flash";
|
||||
muzzleFlash->surface = SurfaceTexture::Stretched(blanktex);
|
||||
muzzleFlash->opaque = false;
|
||||
muzzleFlash->color = glm::vec4(3.0f, 2.2f, 1.0f, 1.0f);
|
||||
muzzleFlash->size = glm::vec3(0.32f);
|
||||
muzzleFlash->speed = 2.5f;
|
||||
muzzleFlash->lifespan = 0.05f;
|
||||
muzzleFlash->emitangle = glm::vec3(0.5f, 0.5f, 0.0f);
|
||||
muzzleFlash->facecamera = true;
|
||||
muzzleFlash->BindToRenderSystem();
|
||||
const int flashesPerShot = 6;
|
||||
|
||||
// Smoke hanging about the barrel afterwards, drifting up
|
||||
ParticleEmitter* muzzleSmoke = new ParticleEmitter();
|
||||
muzzleSmoke->name = "muzzle smoke";
|
||||
muzzleSmoke->surface = SurfaceTexture::Stretched(blanktex);
|
||||
muzzleSmoke->opaque = false;
|
||||
muzzleSmoke->color = glm::vec4(0.55f, 0.55f, 0.55f, 0.35f);
|
||||
muzzleSmoke->size = glm::vec3(0.22f);
|
||||
muzzleSmoke->speed = 1.2f;
|
||||
muzzleSmoke->lifespan = 0.7f;
|
||||
muzzleSmoke->emitangle = glm::vec3(1.2f, 1.2f, 0.0f);
|
||||
muzzleSmoke->gravity = glm::vec3(0.0f, 1.5f, 0.0f);
|
||||
muzzleSmoke->facecamera = true;
|
||||
muzzleSmoke->BindToRenderSystem();
|
||||
const int smokePerShot = 8;
|
||||
|
||||
// Sparks off whoever it hits, back toward the shooter
|
||||
ParticleEmitter* impactSparks = new ParticleEmitter();
|
||||
impactSparks->name = "impact sparks";
|
||||
impactSparks->surface = SurfaceTexture::Stretched(blanktex);
|
||||
impactSparks->opaque = false;
|
||||
impactSparks->color = glm::vec4(2.0f, 1.4f, 0.6f, 1.0f);
|
||||
impactSparks->size = glm::vec3(0.05f);
|
||||
impactSparks->speed = 6.0f;
|
||||
impactSparks->lifespan = 0.2f;
|
||||
impactSparks->emitangle = glm::vec3(1.6f, 1.6f, 0.0f);
|
||||
impactSparks->gravity = glm::vec3(0.0f, -12.0f, 0.0f);
|
||||
impactSparks->facecamera = true;
|
||||
impactSparks->BindToRenderSystem();
|
||||
const int impactSparksPerHit = 24;
|
||||
|
||||
// Lights up everything around the gun for a moment
|
||||
PointLight* muzzleLight = rendersystem->lighting->AddPointLight(glm::vec3(0.0f), glm::vec3(1.0f, 0.7f, 0.35f), 0.0f, 8.0f);
|
||||
muzzleLight->castShadow = false; // too quick to be worth six shadow renders a frame
|
||||
const float muzzleLightIntensity = 160.0f;
|
||||
const float muzzleLightTime = 0.08f; // real seconds
|
||||
float muzzleLightLeft = 0.0f;
|
||||
|
||||
auto UpdateTracers = [&](float dt) {
|
||||
muzzleSparks->Step(dt);
|
||||
muzzleFlash->Step(dt);
|
||||
muzzleSmoke->Step(dt);
|
||||
impactSparks->Step(dt);
|
||||
muzzleLightLeft = std::max(muzzleLightLeft - dt, 0.0f);
|
||||
muzzleLight->intensity = muzzleLightIntensity * (muzzleLightLeft / muzzleLightTime);
|
||||
for (Tracer& tracer : tracers) {
|
||||
if (tracer.life <= 0.0f)
|
||||
continue;
|
||||
@@ -1015,7 +1080,11 @@ void Engine::Initiate()
|
||||
if (tracer.life <= 0.0f) {
|
||||
tracer.life = 0.0f;
|
||||
rendersystem->RemoveRenderable(tracer.mesh);
|
||||
continue;
|
||||
}
|
||||
const float thickness = tracerThickness * (tracer.life / tracerTime);
|
||||
const glm::vec3 scale = tracer.mesh->t.GetScale();
|
||||
tracer.mesh->t.ScaleTo({ thickness, thickness, scale.z });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1042,6 +1111,13 @@ void Engine::Initiate()
|
||||
shot.stun = ability.stun;
|
||||
shot.killAnimation = ability.killAnimation;
|
||||
shot.killAbility = &ability;
|
||||
shot.onHit = [&, direction](Character& target) {
|
||||
const glm::vec3 at = target.body->t.GetTranslation() + glm::vec3(0.0f, target.body->t.GetScale().y * 0.2f, 0.0f);
|
||||
impactSparks->t.TranslateTo(at);
|
||||
impactSparks->t.RotateToQuaternion(LookAt(-direction));
|
||||
for (int i = 0; i < impactSparksPerHit; i++)
|
||||
impactSparks->Emit();
|
||||
};
|
||||
hitboxsystem->Add(shot);
|
||||
ShowTracer(origin, direction, ability.range);
|
||||
|
||||
@@ -1052,6 +1128,16 @@ void Engine::Initiate()
|
||||
muzzleSparks->t.RotateToQuaternion(LookAt(direction));
|
||||
for (int i = 0; i < sparksPerShot; i++)
|
||||
muzzleSparks->Emit();
|
||||
muzzleFlash->t.TranslateTo(origin + direction * 0.15f);
|
||||
muzzleFlash->t.RotateToQuaternion(LookAt(direction));
|
||||
for (int i = 0; i < flashesPerShot; i++)
|
||||
muzzleFlash->Emit();
|
||||
muzzleSmoke->t.TranslateTo(origin);
|
||||
muzzleSmoke->t.RotateToQuaternion(LookAt(direction));
|
||||
for (int i = 0; i < smokePerShot; i++)
|
||||
muzzleSmoke->Emit();
|
||||
muzzleLight->position = origin + direction * 0.3f;
|
||||
muzzleLightLeft = muzzleLightTime;
|
||||
|
||||
// Its own channel every time, so quick shots overlap rather than cutting each other off
|
||||
if (!ability.sound.empty())
|
||||
@@ -1179,7 +1265,7 @@ void Engine::Initiate()
|
||||
auto ApplyAbilityStatus = [&](Character& user, const Ability& ability) {
|
||||
const unsigned int use = user.body->combat.GetAbilityUse();
|
||||
for (const AbilityHit& hit : abilityHits)
|
||||
if (hit.user == &user && hit.use == use && !hit.target->IsDead())
|
||||
if (hit.user == &user && hit.use == use && !hit.target->IsDead() && !hit.target->IsInvulnerable())
|
||||
hit.target->AddStatus(ability.status, ability.statusTime);
|
||||
};
|
||||
|
||||
@@ -1490,6 +1576,7 @@ void Engine::Initiate()
|
||||
// ------------------------------------------------------------------------
|
||||
struct Execution {
|
||||
Character* victim = nullptr;
|
||||
Character* killer = nullptr; // has iframes until it's over, so the cutscene can't be cut short
|
||||
Animation* animation = nullptr;
|
||||
float timeLeft = 0.0f; // real seconds
|
||||
float time = 0.0f; // real seconds since it started
|
||||
@@ -1526,6 +1613,9 @@ void Engine::Initiate()
|
||||
const float ticksPerSecond = animation->GetTicksPerSecond() > 0.0f ? animation->GetTicksPerSecond() : 25.0f;
|
||||
execution.timeLeft = animation->GetDuration() / ticksPerSecond;
|
||||
execution.filmed = (&victim == controlled || killer == controlled) && !IsFilming();
|
||||
execution.killer = const_cast<Character*>(killer);
|
||||
if (execution.killer && execution.killer != &victim)
|
||||
execution.killer->AddStatus(StatusEffect::Type::Invulnerable);
|
||||
if (ability && execution.filmed)
|
||||
execution.sounds = ability->killSounds;
|
||||
executions.push_back(execution);
|
||||
@@ -1560,6 +1650,13 @@ void Engine::Initiate()
|
||||
|
||||
// Over: up again somewhere else, or for those that heal on death, right where they are
|
||||
victim.RemoveStatus(StatusEffect::Type::Executed);
|
||||
if (Character* killer = execution.killer) {
|
||||
// Unless it's in another one of its own
|
||||
const bool stillExecuting = std::any_of(executions.begin(), executions.end(), [&](const Execution& other) {
|
||||
return &other != &execution && other.killer == killer; });
|
||||
if (!stillExecuting)
|
||||
killer->RemoveStatus(StatusEffect::Type::Invulnerable);
|
||||
}
|
||||
if (Animator* action = victim.body->actionAnimator; action && action->m_CurrentAnimation == execution.animation) {
|
||||
action->holdsAtEnd = false;
|
||||
action->StopAnimation();
|
||||
@@ -1865,7 +1962,7 @@ void Engine::Initiate()
|
||||
StartExecution(target, attacker, hitbox.killAnimation, hitbox.killAbility);
|
||||
return;
|
||||
}
|
||||
if (hitbox.knockdown > 0.0f)
|
||||
if (hitbox.knockdown > 0.0f && !target.HasHyperArmor())
|
||||
target.Knockdown(hitbox.knockdown);
|
||||
if (hitbox.onHit)
|
||||
hitbox.onHit(target);
|
||||
|
||||
Reference in New Issue
Block a user