// Game tick handling
// Copyright (C) 2002 David Rosen
// Copyright (C) 2003 Ryan C. Gordon
// Copyright (C) 2003 Dan Olson
// Copyright (C) 2003 Steven Fuller
// Copyright (C) 2003 Zachary Jack Slater
// Copyright (C) 2003 Toby Haynes
// Copyright (C) 2021-2023 Nguyễn Gia Phong
//
// This file is part of Black Shades.
//
// Black Shades 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.
//
// Black Shades 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 Black Shades. If not, see .
#include
#include
#include "Game.h"
#include "misc.h"
extern float multiplier;
extern int thirdperson;
extern bool visions;
extern Sprites sprites;
extern unsigned int gSourceID[37];
extern Camera camera;
extern float camerashake;
extern int environment;
extern float precipitationhorz;
extern float precipitationvert;
extern float snowdelay;
extern float precipitationdensity;
extern float soundscalefactor;
extern int slomo;
#define maxfallvel 40
void Splat(Game* game, int k)
{
if (k && visions)
return;
auto& person = game->person[k];
if (person.velocity.y > -maxfallvel) {
person.velocity.y = 0;
return;
}
auto& skeleton = person.skeleton;
skeleton.free = 1;
skeleton.offset = 0;
person.health = 0;
person.longdead = person.bleeding = person.bleeddelay = 1;
person.DoAnimations(k);
auto& joints = skeleton.joints;
auto& head_joint = joints[head];
person.bjoint1 = &head_joint;
person.bjoint2 = joints + neck;
for (auto& joint : joints) {
joint.position = rotate(joint.position + joint.offset, 0,
person.playerrotation, 0) + person.playercoords;
joint.realoldposition = joint.position;
joint.velocity = {0, person.velocity.y, 0};
}
auto soundpos = head_joint.position - camera.position;
ALfloat gLoc[] {
soundpos.x / soundscalefactor,
soundpos.y / soundscalefactor,
soundpos.z / soundscalefactor,
};
alSourcefv(gSourceID[headwhacksound], AL_POSITION, gLoc);
alSourcePlay(gSourceID[headwhacksound]);
}
void updateSong(Game* game)
{
if (environment == rainy_environment)
alSourcePlay(gSourceID[rainsound]);
else
alSourceStop(gSourceID[rainsound]);
if (!game->musictoggle)
return;
alSourceStop(gSourceID[game->whichsong]);
if (game->type == zombie_type)
game->whichsong = zombiesong;
else
game->whichsong = assassinsong;
alSourcef(gSourceID[game->whichsong], AL_PITCH, 1.0f);
alSourcePlay(gSourceID[game->whichsong]);
}
void click(Game* game, int button, int action, int mods)
{
if (game->menu) {
if (button != GLFW_MOUSE_BUTTON_LEFT)
return;
double xpos, ypos;
auto window = glfwGetCurrentContext();
glfwGetCursorPos(window, &xpos, &ypos);
double mousex = xpos * 640 / game->screenwidth;
double mousey = 480 - ypos * 480 / game->screenheight;
auto button = 0;
if (mousex > 120 && mousex < 560) {
if (mousey > 235 && mousey < 305)
button = 1;
else if (mousey > 112 && mousey < 182)
button = 2;
}
if (action == GLFW_PRESS) {
game->mouseoverbutton = button;
return;
} else if (button != game->mouseoverbutton) {
game->mouseoverbutton = 0;
}
switch (game->mouseoverbutton) {
case 1:
game->flashr = game->flashg = game->flashb = 1.0f;
game->flashamount = 1.0f;
alSourcePlay(gSourceID[soulinsound]);
if (!game->gameinprogress) {
game->mission = 0;
initGame(game);
}
updateSong(game);
if (visions)
alSourcePlay(gSourceID[visionsound]);
game->gameinprogress = true;
setMenu(game, false);
break;
case 2:
if (game->gameinprogress) {
game->flashr = game->flashg = game->flashb = 1.0f;
game->flashamount = 1.0f;
game->gameinprogress = false;
} else {
game->flashamount = game->flashr = 1.0f;
game->flashg = game->flashb = 0.0f;
glfwSetWindowShouldClose(window, true);
}
alSourcePlay(gSourceID[losesound]);
break;
}
game->mouseoverbutton = 0;
return;
}
auto& player = game->person[0];
auto& weapon = player.whichgun;
if (visions) {
if (action == GLFW_PRESS)
alSourcePlay(gSourceID[soulinsound]);
return;
}
XYZ facing {0, 0, -1};
facing = rotate(facing, -camera.rotation2, 0, 0);
facing = rotate(facing, 0, -camera.rotation, 0);
XYZ flatfacing = facing;
flatfacing.y = 0;
flatfacing = normalize(flatfacing);
// Gun whacking or knife slashing
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS
&& player.attackframe < 0
&& (!player.aiming || player.ammo <= 0
|| weapon == nogun || weapon == knife
|| player.targetanimation == joganim)
&& player.targetanimation != diveanim
&& player.targetanimation != throwanim) {
bool attacking = false;
float closedistance = 0.0f;
for (int i = 1; i < game->numpeople; ++i) {
auto& person = game->person[i];
if (person.type == viptype || person.skeleton.free)
continue;
auto distance = sqrlen(player.playercoords + flatfacing
- person.playercoords);
if (distance > 12 + (weapon == knife) * 10)
continue;
if (closedistance == 0 || distance < closedistance) {
attacking = true;
player.killtarget = i;
closedistance = distance;
}
}
if (attacking) {
player.attacktarget = 0;
player.attackframe = 0;
return;
}
}
// Disarming
if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS
&& player.attackframe < 0
&& (!player.aiming || weapon == nogun || weapon == knife)
&& (player.targetanimation == idleanim
|| player.targetanimation == walkanim
|| player.targetanimation == joganim)) {
bool attacking = false;
float closedistance = 0.0f;
for (int i = 1; i < game->numpeople; ++i) {
auto& person = game->person[i];
if (person.skeleton.free || person.whichgun == nogun)
continue;
auto distance = sqrlen(player.playercoords + flatfacing
- person.playercoords);
if (distance > 12)
continue;
attacking = true;
if (closedistance == 0 || distance < closedistance) {
player.killtarget = i;
closedistance = distance;
}
}
if (attacking) {
auto& target = game->person[player.killtarget];
weapon = target.whichgun;
player.ammo = target.ammo;
player.aiming = true;
target.whichgun = nogun;
target.killtarget = -1;
target.targetframe = player.targetframe = 0;
target.targetanimation = player.targetanimation = throwanim;
target.target = player.target = 1;
target.playercoords = player.playercoords;
target.playerrotation = player.playerrotation;
target.speed = player.speed = 1;
target.speedmult = 1;
game->score += 150;
return;
}
}
// Pulling gun trigger
if (player.aiming && player.targetanimation != joganim
&& weapon != nogun && weapon != knife && weapon != grenade) {
if (button == GLFW_MOUSE_BUTTON_RIGHT && weapon == sniperrifle)
game->zoom = action == GLFW_PRESS;
else if (button == GLFW_MOUSE_BUTTON_LEFT)
player.firing = action == GLFW_PRESS;
else if (button == GLFW_MOUSE_BUTTON_MIDDLE && game->debug)
player.firing = player.grenphase = action == GLFW_PRESS;
return;
}
// Grenade
if (weapon == grenade && player.ammo > 0
&& player.reloading <= 0 && player.attackframe < 0) {
auto& skeleton = player.skeleton;
auto& hand = skeleton.joints[righthand];
auto soundsrc = rotate(hand.position, 0, player.playerrotation, 0)
+ player.playercoords - camera.position;
if (player.grenphase) {
if (button == GLFW_MOUSE_BUTTON_LEFT
&& action == GLFW_RELEASE) { // throw
player.grenphase = false;
player.attackframe = 0;
player.attacktarget = 0;
player.killtarget = 0;
} else if (button == GLFW_MOUSE_BUTTON_RIGHT
&& action == GLFW_PRESS) { // put pin back
player.grenphase = false;
playSound(gSourceID[pinreplacesound],
soundsrc.x, soundsrc.y, soundsrc.z);
}
} else {
if (button == GLFW_MOUSE_BUTTON_LEFT
&& action == GLFW_PRESS) { // pull pin
player.grenphase = true;
playSound(gSourceID[pinpullsound],
soundsrc.x, soundsrc.y, soundsrc.z);
}
}
}
}
void look(Game* game, double xpos, double ypos)
{
if (game->menu)
return;
int width, height;
const auto window = glfwGetCurrentContext();
glfwGetWindowSize(window, &width, &height);
int xcenter = width / 2, ycenter = height / 2;
glfwSetCursorPos(window, xcenter, ycenter); // we shouldn't need this
auto& player = game->person[0];
auto factor = game->mouse_sensitivity;
if (player.aimamount >= 1)
factor *= (game->zoom) ? 0.05 : 0.8;
if (slomo == 2)
factor *= 0.6;
camera.rotation += (xpos - xcenter) * factor;
camera.rotation2 += (ypos - ycenter) * factor;
// Smoothen camera movements
camera.rotation = camera.rotation * 0.7 + camera.oldrotation * 0.3;
camera.oldrotation = camera.rotation;
player.playerrotation = 180 - camera.rotation;
camera.rotation2 = camera.rotation2 * 0.7 + camera.oldrotation2 * 0.3;
camera.rotation2 = std::min(std::max(camera.rotation2, -89.0f), 89.0f);
camera.oldrotation2 = camera.rotation2;
}
void setListener(Game* game, XYZ facing)
{
XYZ upvector {0, 0, -1};
upvector = rotate(upvector, -camera.rotation2 + 90, 0, 0);
upvector = rotate(upvector, 0, -camera.rotation, 0);
ALfloat ori[] {
facing.x, facing.y, facing.z,
-upvector.x, upvector.y, upvector.z,
};
alListenerfv(AL_ORIENTATION, ori);
}
XYZ aimPlayer(Game* game)
{
auto& joints = game->person[0].skeleton.joints;
auto point = joints[lefthand].position - joints[righthand].position;
float aimrot = 0.0f, aimrot2 = 0.0f;
switch (game->person[0].whichgun) {
case assaultrifle:
aimrot = -2.5f;
break;
case sniperrifle:
aimrot = 4.0f;
break;
case shotgun:
aimrot = randInt(-1000, 1000) / 500.0f - 1;
aimrot2 = randInt(-1000, 1000) / 500.0f + 2;
break;
case handgun1:
case handgun2:
aimrot = -0.9f;
auto k = thirdperson ? 0.35 : 0.65;
point = joints[righthand].position
- joints[head].position * k
- joints[neck].position * (1 - k);
break;
}
return rotate(point, aimrot2,
game->person[0].playerrotation + aimrot, 0);
}
XYZ aimBot(Game* game, int j)
{
auto& bot = game->person[j];
float inaccuracy = 0.0f;
switch (bot.whichgun) {
case handgun1:
case handgun2:
inaccuracy = 8.0f;
break;
case assaultrifle:
case shotgun:
inaccuracy = 6.0f;
break;
case sniperrifle:
inaccuracy = 2.0f;
break;
}
auto& target = game->person[bot.killtarget];
if (target.skeleton.free)
inaccuracy *= 3;
auto& joints = target.skeleton.joints;
XYZ aim = joints[abdomen].position;
if (target.skeleton.free)
inaccuracy *= 3;
else
aim = rotate(aim, 0, target.playerrotation, 0)
+ target.playercoords;
auto& lefthandpos = joints[lefthand].position;
aim -= bot.playercoords
+ rotate(lefthandpos, 0, bot.playerrotation, 0);
return rotate(
rotate(rotate(aim, 0, -bot.playerrotation, 0),
randFloat() * inaccuracy, randFloat() * inaccuracy, 0),
0, bot.playerrotation, 0);
}
void nextLevel(Game* game)
{
game->score += ++game->mission * 50 + 100;
game->highscore = std::max(game->score, game->highscore);
game->flashamount = game->flashg = 1;
game->flashr = game->flashb = 0;
alSourcePlay(gSourceID[souloutsound]);
if (game->mission >= game->nummissions) {
alSourceStop(gSourceID[rainsound]);
alSourceStop(gSourceID[visionsound]);
if (game->musictoggle) {
alSourceStop(gSourceID[game->whichsong]);
game->whichsong = mainmenusong;
alSourcef(gSourceID[game->whichsong], AL_PITCH, 1);
alSourcePlay(gSourceID[game->whichsong]);
}
setMenu(game, game->beatgame = true);
game->gameinprogress = 0;
} else {
initGame(game);
updateSong(game);
}
}
void checkPersonCollisions(Game* game, int k)
{
auto& person = game->person[k];
if (k != 0 || !visions)
person.oldplayercoords = person.playercoords;
person.DoStuff(k);
if (person.skeleton.free)
return;
person.onground = false;
auto overpoint = person.playercoords;
overpoint.y += 3000;
auto underpoint = person.playercoords;
underpoint.y -= 3000;
const int x = (person.playercoords.x + block_spacing / 2) / block_spacing;
const int z = (person.playercoords.z + block_spacing / 2) / block_spacing;
const int endx = std::min(num_blocks - 1, x + 1);
const int endz = std::min(num_blocks - 1, z + 1);
/* TODO: huh?
* if(k!=0) {
* beginx == person.whichblockx;
* beginz == person.whichblocky;
* endx == person.whichblockx;
* endz == person.whichblocky;
* }
*/
for (auto i = std::max(0, x); i <= endx; ++i)
for (auto j = std::max(0, z); j <= endz; ++j) {
// Ground collision
const auto city_rot = game->cityrotation[i][j] * 90;
const XYZ move = {(float) i * block_spacing,
0.0f, (float) j * block_spacing};
XYZ collpoint;
if (segCrossModelTrans(overpoint, underpoint,
&game->sidewalkcollide, move, city_rot,
&collpoint)
== -1)
continue;
if (person.playercoords.y <= collpoint.y
&& person.velocity.y <= 0) {
person.playercoords.y = collpoint.y;
person.onground = true;
Splat(game, k);
}
if (k != 0)
continue;
// Wall collision
const auto city_type = game->citytype[i][j];
for (auto& bound : game->boundingpoints) {
auto pointnum = k + 1;
if (pointnum > 3)
pointnum = 0;
const auto whichtri = segCrossModelTrans(person.playercoords + bound,
person.playercoords + game->boundingpoints[pointnum],
game->blockwalls + city_type,
move, city_rot, &collpoint);
if (whichtri == -1)
continue;
person.playercoords += rotate(game->blockwalls[city_type].faces[whichtri][0].normal, 0, city_rot, 0);
}
}
if (person.playercoords.y <= 0) {
person.onground = true;
person.playercoords.y = 0;
Splat(game, k);
}
}
void spawnNpc(Game* game)
{
// Pick a random unfull block close to the player
int blockspawnx, blockspawny;
auto block = game->person[0].playercoords / block_spacing;
auto npc_type = (game->type == zombie_type) ? zombietype
: randUint(game->evilprobability) ? civiliantype : eviltype;
auto& vip = game->person[1];
for (auto i = 0; i < 42; ++i) {
blockspawnx = block.x + 0.5f + randInt(-1, 1);
blockspawny = block.z + 0.5f + randInt(-1, 1);
if ((game->citypeoplenum[blockspawnx][blockspawny]
< max_people_block)
&& (npc_type == civiliantype
|| blockspawnx != vip.whichblockx
|| blockspawny != vip.whichblocky))
break;
}
if (game->citypeoplenum[blockspawnx][blockspawny] >= max_people_block
|| (game->spawndelay -= multiplier) > 0)
return;
size_t k = 0;
if (game->numpeople < max_people)
k = game->numpeople++;
else
for (auto i = 0; i < game->numpeople; ++i) {
auto p = game->person + randInt(2, game->numpeople - 1);
if (abs(p->whichblockx - block.x) > 2
&& abs(p->whichblocky - block.z) > 2) {
k = p - game->person;
break;
}
}
if (k == 0)
return;
game->citypeoplenum[blockspawnx][blockspawny]++;
game->spawndelay = 0.1f;
auto& npc = game->person[k];
npc.whichblockx = blockspawnx;
npc.whichblocky = blockspawny;
npc.whichcostume = casualcostumes + randUint(numcasual);
switch (npc.type = npc_type) {
case zombietype:
npc.aiming = false;
npc.existing = true;
npc.pathsize = 1.04;
npc.speedmult = 0.7f + 0.2f * game->difficulty;
npc.targetanimation = zombiewalkanim;
break;
case civiliantype:
npc.aiming = false;
npc.existing = false;
npc.pathsize = 0.98f + 0.04f * randFloat();
npc.speedmult = 0.8f + 0.4f * randFloat();
npc.targetanimation = walkanim;
break;
case eviltype:
npc.aiming = true;
npc.existing = true;
npc.pathsize = 1.04;
npc.speedmult = 1.0f + 0.3f * game->difficulty;
npc.targetanimation = walkanim;
break;
default: break;
}
npc.oldoldoldpathnum = npc.oldoldpathnum = npc.oldpathnum = -1;
npc.pathnum = randUint(8);
npc.pathtarget.x = DIRECTIONS[npc.pathnum].x;
npc.pathtarget.z = DIRECTIONS[npc.pathnum].z;
npc.pathtarget *= npc.pathsize;
npc.pathtarget.x += npc.whichblockx * block_spacing;
npc.pathtarget.z += npc.whichblocky * block_spacing;
npc.oldplayercoords = npc.playercoords = npc.pathtarget;
npc.skeleton.free = false;
npc.speed = 1;
npc.health = npc.maxhealth = 100;
npc.playerrotation = 0;
npc.playerrotation2 = 0;
npc.lastdistancevictim = 200000;
npc.whichgun = nogun;
npc.killtarget = (game->enemystate == 2
&& (npc.type == zombietype || npc.type == eviltype)) ? 1 : -1;
}
void attackCloseRange(Game* game, XYZ flatfacing)
{
auto& player = game->person[0];
if ((player.attackframe < 0 || player.currentanimation != joganim)
&& player.attackframe <= 1 && player.targetanimation != joganim
&& player.whichgun != nogun && player.whichgun != knife
&& player.aiming)
return;
if (player.killtarget < 2)
return;
auto& target = game->person[player.killtarget];
player.killtarget = 0;
if (target.skeleton.free)
return;
target.skeleton.free = true;
target.longdead = 1.0f;
if (target.type == zombietype) {
target.health -= 200;
target.maxhealth -= 20;
} else {
target.health -= 100;
}
for (auto& joint : target.skeleton.joints) {
joint.position = rotate(joint.position,
0, target.playerrotation, 0);
joint.position += target.playercoords;
joint.realoldposition = joint.position;
joint.velocity = target.velocity;
joint.velocity.x += randInt(-4, 4);
joint.velocity.y += randInt(-4, 4);
joint.velocity.z += randInt(-4, 4);
}
auto soundpos = player.playercoords + flatfacing
- camera.position;
if (player.whichgun == knife) {
playSound(gSourceID[knifeslashsound],
soundpos.x, soundpos.y, soundpos.z);
target.bjoint1=&target.skeleton.joints[neck];
target.bjoint2=&target.skeleton.joints[neck];
target.bleeding=1;
target.bleeddelay=1;
player.bjoint1=&player.skeleton.joints[righthand];
player.bjoint2=&player.skeleton.joints[righthand];
player.bleeding=1;
player.bleeddelay=1;
auto velocity = rotate(flatfacing, 0, 70, 0) * 50
+ player.velocity * 2;
velocity.y += 30;
auto sprite_pos = target.playercoords
+ rotate(target.skeleton.joints[neck].position,
0, target.playerrotation, 0);
for (auto i = 2; i < 5; ++i)
sprites.MakeSprite(bloodspritedown,
0.8f, 1.0f, 0.2f, 0.2f, sprite_pos,
velocity * (0.5f - 0.1f * i), i);
} else {
playSound(gSourceID[headwhacksound],
soundpos.x, soundpos.y, soundpos.z);
target.skeleton.joints[head].velocity
+= rotate(flatfacing, 0, 40, 0) * 50
+ player.velocity * 2;
}
}
void tackle(Game* game, XYZ flatfacing)
{
auto& player = game->person[0];
for (int i = 1; i < game->numpeople; ++i) {
auto& person = game->person[i];
if (person.skeleton.free
|| sqrlen(player.playercoords + flatfacing
- person.playercoords) > 22)
continue;
float gLoc[] {
flatfacing.x / soundscalefactor / 2,
flatfacing.y / soundscalefactor / 2,
flatfacing.z / soundscalefactor / 2,
};
alSourcefv(gSourceID[headwhacksound], AL_POSITION, gLoc);
alSourcePlay(gSourceID[headwhacksound]);
person.skeleton.free = true;
person.longdead = 1;
for (auto& joint : person.skeleton.joints) {
joint.position = person.playercoords
+ rotate(joint.position,
0, person.playerrotation, 0);
joint.realoldposition = joint.position;
joint.velocity = player.velocity;
joint.velocity.x += randInt(-4, 4);
joint.velocity.y = randInt(-4, 4) - 10;
joint.velocity.z += randInt(-4, 4);
}
}
}
void bleed(Game* game, size_t i)
{
auto& person = game->person[i];
if (person.health < 100.0f && person.type != zombietype)
person.health -= multiplier * 120.0f;
if (person.health < 0.0f && person.longdead < 0.0f
&& !person.firstlongdead && person.type != zombietype) {
int x = person.whichblockx, y = person.whichblocky;
auto rot = game->cityrotation[x][y] * 90;
auto overpoint = person.skeleton.joints[abdomen].position;
overpoint.y += 3000;
auto underpoint = person.skeleton.joints[abdomen].position;
underpoint.y -= 3000;
XYZ loc;
XYZ move {(float) x * block_spacing, 0.0f, (float) y * block_spacing, };
auto whichtri = segCrossModelTrans(overpoint, underpoint,
&game->sidewalkcollide, move, rot, &loc);
assert(whichtri >= 0);
addDecal(game->decals, BLOOD_POOL, loc, 12, {0.0f, 1.0f, 0.0f},
whichtri, &game->sidewalkcollide, move, rot);
person.firstlongdead = true;
return;
}
if (person.bleeding <= 0.0f)
return;
person.bleeding -= multiplier;
person.bleeddelay -= multiplier * 10.0f;
if (person.bleeddelay > 0.0f)
return;
person.bleeddelay = 1.0f;
XYZ loc = (person.bjoint1->position + person.bjoint2->position) / 2.0f;
sprites.MakeSprite(bloodspritedown, 0.6f, 1.0f, 0.2f, 0.2f,
person.skeleton.free ? loc : rotate(loc,
0.0f, person.playerrotation, 0.0f) + person.playercoords,
{}, person.bleeding * 3.0f);
}
void heal(Game* game, size_t i)
{
auto& zombie = game->person[i];
if (zombie.type != zombietype)
return;
zombie.maxhealth = std::min(100.0f,
zombie.maxhealth + multiplier * 2.0f);
zombie.health = std::min(zombie.maxhealth,
zombie.health + multiplier * 10.0f);
float slow = 0.6f, fast = 0.7f + game->difficulty * 0.2f;
if (zombie.health < 40.0f)
zombie.speedmult = std::max(slow,
zombie.speedmult - multiplier * 0.05f);
else
zombie.speedmult = std::min(fast,
zombie.speedmult + multiplier * 0.025f);
if (zombie.speedmult < slow)
zombie.killtarget = -1;
else if (zombie.speedmult > fast)
zombie.killtarget = 1;
}
void recoil(Game* game, size_t i)
{
auto& person = game->person[i];
switch (person.type) {
case playertype:
switch (person.whichgun) {
case shotgun:
person.recoil -= multiplier * 4;
break;
case sniperrifle:
person.recoil -= multiplier * 2;
break;
case handgun1:
case handgun2:
person.recoil -= multiplier * 5;
break;
case assaultrifle:
person.recoil -= multiplier * 10;
break;
}
if (!person.aiming || visions)
game->zoom = false;
break;
case eviltype:
switch (person.whichgun) {
case shotgun:
case sniperrifle:
person.recoil -= multiplier * 1;
break;
case handgun1:
case handgun2:
person.recoil -= multiplier * 2;
break;
case assaultrifle:
person.recoil -= multiplier * 10;
break;
}
break;
default: break;
}
if (person.recoil < 0)
person.recoil = 0;
}
void controlZombie(Game* game, size_t i)
{
auto& zombie = game->person[i];
if (zombie.type != zombietype)
return;
auto& vip = game->person[1];
if (sqrlen(zombie.playercoords - vip.playercoords) > 20000
|| zombie.speedmult < 0.7f) {
zombie.killtarget = -1;
return;
} else if (game->enemystate == 2) {
zombie.killtarget = 1;
}
auto& target = game->person[zombie.killtarget];
if (zombie.targetanimation != zombieeatanim || target.eaten != i)
return;
auto& target_joints = target.skeleton.joints;
target_joints[head].locked = true;
target_joints[rightshoulder].locked = true;
auto& zombie_joints = zombie.skeleton.joints;
target_joints[head].position = zombie.playercoords
+ rotate(zombie_joints[righthand].position,
0.0f, zombie.playerrotation, 0.0f);
target_joints[head].velocity = {};
target_joints[rightshoulder].position = zombie.playercoords
+ rotate(zombie_joints[lefthand].position,
0.0f, zombie.playerrotation, 0.0f);
target_joints[rightshoulder].velocity = {};
target.skeleton.DoConstraints();
size_t x = zombie.whichblockx, y = zombie.whichblocky;
auto& collide = game->blocksimplecollide[game->citytype[x][y]];
XYZ tmp;
auto rotation = game->cityrotation[x][y] * 90.0f;
target.skeleton.DoConstraints(&collide, &tmp, rotation);
target.skeleton.joints[rightshoulder].locked = false;
target.skeleton.joints[head].locked = false;
target.longdead = 1.0f;
}
void renderLaser(Game* game)
{
auto& player = game->person[0];
if (visions || !player.aiming)
return;
switch (player.currentanimation) {
case walkanim:
case idleanim:
break;
default:
return;
}
XYZ aim;
float coeff;
auto& joints = player.skeleton.joints;
auto& rotation = player.playerrotation;
switch (player.whichgun) {
case assaultrifle:
case shotgun:
aim = rotate(joints[lefthand].position
- joints[righthand].position,
0.0f, rotation - 2.5f, 0.0f);
coeff = 0.15f;
break;
case handgun1:
case handgun2:
aim = rotate(joints[righthand].position
- joints[head].position * 0.35f
- joints[neck].position * 0.65f,
0.0f, player.playerrotation - 0.9f, 0.0f);
coeff = -0.15f;
break;
default:
return;
}
aim = normalize(aim);
auto& coords = player.playercoords;
auto start = coords
+ rotate(joints[lefthand].position, 0.0f, rotation, 0.0f)
+ rotate(rotate(rotate(aim, 0.0f, -rotation, 0.0f),
90.0f, 0.0f, 0.0f),
0.0f, rotation, 0) * coeff;
auto end = start + aim * 1000.0f;
// Blocks
int x = coords.x / block_spacing + 0.5f;
int z = coords.z / block_spacing + 0.5f;
for (int i = std::max(0, x - 2); i < std::min(num_blocks, x + 3); ++i)
for (int j = std::max(0, z - 2); j < std::min(num_blocks, z + 3); ++j) {
XYZ move = {
(float) i * block_spacing,
0.0f,
(float) j * block_spacing,
};
XYZ tmp {};
auto& block = game->blocks[game->citytype[i][j]];
if (segCrossModelTrans(start, end, &block,
move, game->cityrotation[i][j]*90, &tmp)
> -1)
end = tmp;
}
XYZ tmp;
if (segCrossModelTrans(start, end, &game->Bigstreet,
{camera.position.x, 0.0f, camera.position.z}, 0, &tmp)
> -1)
end = tmp;
// People
float olddistance = 0.0f;
int whichhit = -1;
for (auto i = 1; i < game->numpeople; ++i) {
auto& person = game->person[i];
if (sqrlen(coords - person.playercoords) > 20000)
continue;
auto hit = person.BulletCollideWithPlayer(i, start, end);
if (!hit.collision)
continue;
auto distance = sqrlen(start - hit.hitlocation);
if (distance < olddistance || whichhit == -1) {
olddistance = distance;
whichhit = i;
end = hit.hitlocation;
}
}
sprites.MakeSprite(bulletinstant, 0.4f, 1, 0, 0, start, end, 0.2f);
}
void Game::Tick()
{
if (person[1].health <= 0 || person[0].health <= 0)
losedelay -= multiplier / 6;
else
timeremaining -= multiplier;
if (timeremaining <= 0)
nextLevel(this);
if (losedelay <= 0) {
score -= (person[murderer].health > 0) ? 200 : 100;
flashamount = 1;
flashr = flashg = flashb = 0;
alSourcePlay(gSourceID[soulinsound]);
initGame(this);
updateSong(this);
}
spawnNpc(this);
sprites.DoStuff();
// Facing
XYZ facing {0, 0, -1};
facing = rotate(facing, -camera.rotation2, 0, 0);
facing = rotate(facing, 0, -camera.rotation, 0);
XYZ flatfacing = facing;
flatfacing.y = 0;
flatfacing = normalize(flatfacing);
// Check collision with buildings
int beginx,endx;
int beginz,endz;
XYZ move;
int whichtri;
XYZ underpoint;
XYZ overpoint;
for (auto k = numpeople; k--; checkPersonCollisions(this, k));
// Camera
camera.oldposition = camera.position;
camera.targetoffset = {0.0f, 0.0f, -5.0f};
XYZ towards;
XYZ finaltarget;
XYZ blah;
int closesttarget = 0;
float leastdistance = 0.0;
float tooclose;
float toofar;
//People
for (size_t i = 0; i < numpeople; ++i) {
bleed(this, i);
heal(this, i);
if (!person[i].skeleton.free) {
recoil(this, i);
controlZombie(this, i);
bool realcheck = false;
// Pathfinding
if (i > 0 && person[i].targetanimation != getupfrontanim
&& person[i].targetanimation != thrownanim
&& person[i].targetanimation != getupbackanim
&& person[i].currentanimation != getupfrontanim
&& person[i].currentanimation != getupbackanim) {
person[i].pathcheckdelay -= multiplier;
// Realcheck tells us
// a) we've got close to the end of our path or
// b) we're moving away from our target
auto moving_away = sqrlen(person[i].pathtarget - person[i].playercoords)
> sqrlen(person[i].pathtarget - person[i].oldplayercoords);
realcheck = (abs(person[i].playercoords.x
- person[i].pathtarget.x) < 1
&& abs(person[i].playercoords.z
- person[i].pathtarget.z) < 1)
|| moving_away;
if(person[i].targetanimation==idleanim&&person[i].killtargetvisible==0){
person[i].targetanimation=walkanim;
if(person[i].type==zombietype)person[i].targetanimation=zombiewalkanim;
realcheck=1;
}
if((realcheck||((person[i].killtarget>-1&&person[i].type!=civiliantype)&&person[i].pathcheckdelay<=0)||person[i].killtargetvisible)){
person[i].pathcheckdelay = 1.2f;
if ((person[i].killtarget == -1
|| person[i].type == civiliantype)
&& !person[i].running) {
person[i].killtargetvisible = 0;
leastdistance = 2000000;
for (unsigned char j = 0; j < 8; j++) {
person[i].pathtarget = DIRECTIONS[j];
person[i].pathtarget *= person[i].pathsize;
person[i].pathtarget.x += person[i].whichblockx*block_spacing;
person[i].pathtarget.z += person[i].whichblocky*block_spacing;
if (sqrlen(person[i].playercoords - person[i].pathtarget) < leastdistance
&& j != person[i].oldpathnum
&& j != person[i].oldoldpathnum
&& j != person[i].oldoldoldpathnum){
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
closesttarget = j;
}
}
if (closesttarget >= 0 && closesttarget < 8) {
person[i].oldoldoldpathnum = person[i].oldoldpathnum;
person[i].oldoldpathnum = person[i].oldpathnum;
person[i].oldpathnum = person[i].pathnum;
person[i].pathnum = closesttarget;
person[i].pathtarget = DIRECTIONS[closesttarget];
person[i].pathtarget *= person[i].pathsize;
person[i].pathtarget.x += person[i].whichblockx*block_spacing;
person[i].pathtarget.z += person[i].whichblocky*block_spacing;
}
}
if(person[i].running&&realcheck){
person[i].killtargetvisible=0;
person[i].targetanimation=joganim;
// Dead target?
if (person[person[i].killtarget].health <= 0)
person[i].running = 0;
person[i].killtarget=1;
// If pathfind
if (realcheck) {
leastdistance = 2000000;
person[i].lastdistancevictim = 0;
closesttarget = -1;
// Check best path
closesttarget = person[i].pathnum;
// Check other blocks?
if (closesttarget == person[i].pathnum) {
beginx = person[i].whichblockx-2;
if(beginx<0)beginx=0;
beginz=person[i].whichblocky-2;
if(beginz<0)beginz=0;
endx=person[i].whichblockx+2;
if(endx>num_blocks-1)endx=num_blocks-1;
endz=person[i].whichblocky+2;
if(endz>num_blocks-1)endz=num_blocks-1;
leastdistance=2000000;
for (int l = beginx; l <= endx; l++) {
for (int m = beginx; m <= endx; m++) {
for (unsigned char j = 0; j < 8; ++j) {
person[i].pathtarget = DIRECTIONS[j];
person[i].pathtarget*=person[i].pathsize;
person[i].pathtarget.x+=l*block_spacing;
person[i].pathtarget.z+=m*block_spacing;
if ((sqrlen(person[i].playercoords - person[i].pathtarget)
< leastdistance)
&& (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
< sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords))
&& (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
== -1)
&& (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
move, cityrotation[l][m], &blah)
== -1)) {
person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
closesttarget=j;
finaltarget=person[i].pathtarget;
person[i].whichblockx=l;
person[i].whichblocky=m;
}
}
}
}
}
if (closesttarget != -1) {
person[i].pathnum = closesttarget;
person[i].pathtarget = finaltarget;
}
}
}
// Assassin
if((person[i].killtarget>-1&&person[i].type!=civiliantype)&&!person[i].running){
// Dead target?
if(person[person[i].killtarget].health<=0&&person[i].type==eviltype){
person[i].playerrotation2 = 0;
person[i].whichgun = nogun;
person[i].targetanimation = walkanim;
person[i].lastdistancevictim = 200000;
person[i].pathnum = -1;
enemystate = 1;
person[i].killtarget = -1;
realcheck = 1;
}
if(person[i].type==zombietype&&person[person[i].killtarget].health<=0){
if(person[person[i].killtarget].eaten!=i){
person[i].playerrotation2 = 0;
person[i].targetanimation = zombiewalkanim;
person[i].lastdistancevictim = 200000;
person[i].pathnum = -1;
realcheck = 1;
person[i].killtarget = -1;
}
if(person[person[i].killtarget].eaten == i && person[i].targetanimation != zombieeatanim) {
person[i].targetanimation = zombieeatanim;
person[i].targetframe = 0;
person[i].target = 0;
}
enemystate = 1;
}
if(person[person[i].killtarget].health>0){
if(person[person[i].killtarget].skeleton.free){
person[person[i].killtarget].playercoords=person[person[i].killtarget].averageloc;
}
// If pathfind
if(realcheck){
leastdistance=2000000;
person[i].lastdistancevictim=2000000;
closesttarget=-1;
// Check best path
for (unsigned char j = 0; j < 8; ++j) {
person[i].pathtarget = DIRECTIONS[j];
person[i].pathtarget*=person[i].pathsize;
person[i].pathtarget.x+=person[i].whichblockx*block_spacing;
person[i].pathtarget.z+=person[i].whichblocky*block_spacing;
if ((sqrlen(person[i].playercoords - person[i].pathtarget)
< leastdistance)
&& (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
< person[i].lastdistancevictim)
&& (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
== -1)) {
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
closesttarget=j;
finaltarget=person[i].pathtarget;
}
}
//Check other blocks?
if((closesttarget==person[i].pathnum)||closesttarget==-1){
beginx=person[i].whichblockx-2;
if(beginx<0)beginx=0;
beginz=person[i].whichblocky-2;
if(beginz<0)beginz=0;
endx=person[i].whichblockx+2;
if(endx>num_blocks-1)endx=num_blocks-1;
endz=person[i].whichblocky+2;
if(endz>num_blocks-1)endz=num_blocks-1;
leastdistance=2000000;
for(int l=beginx;l<=endx;l++){
for(int m=beginx;m<=endx;m++){
if(l!=person[i].whichblockx||m!=person[i].whichblocky){
for (unsigned char j = 0; j < 8; ++j){
person[i].pathtarget = DIRECTIONS[j];
person[i].pathtarget*=person[i].pathsize;
person[i].pathtarget.x+=l*block_spacing;
person[i].pathtarget.z+=m*block_spacing;
if ((sqrlen(person[i].playercoords - person[i].pathtarget)
< leastdistance)
&& (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
< sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords))
&& (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
== -1)
&& (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
move, cityrotation[l][m], &blah)
== -1)) {
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
closesttarget=j;
finaltarget=person[i].pathtarget;
person[i].whichblockx=l;
person[i].whichblocky=m;
}
}
}
}
}
}
if(closesttarget!=-1){
person[i].onpath=1;
person[i].pathnum=closesttarget;
person[i].pathtarget=finaltarget;
}
}
//Check killtargetvisible
person[i].killtargetvisible=1;
if(person[person[i].killtarget].health<=0)person[i].killtargetvisible=0;
if (closesttarget != -1
&& sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) > 30000)
person[i].killtargetvisible = 0;
if (person[i].killtarget == 0 && visions)
person[i].killtargetvisible = 0;
if(person[i].killtargetvisible){
beginx = std::max(0,
person[i].whichblockx - 2);
beginz = std::max(0,
person[i].whichblocky - 2);
endx = std::min(num_blocks - 1,
person[i].whichblockx + 2);
endz = std::min(num_blocks - 1,
person[i].whichblocky + 2);
for (auto l = beginx; l <= endx; ++l)
for (auto m = beginx; m <= endx; ++m)
if (person[i].killtargetvisible
&& (segCrossModelTrans(person[i].playercoords,
person[person[i].killtarget].playercoords,
&blocksimple,
{(float) l * block_spacing,
-3.0f,
(float) m * block_spacing},
cityrotation[l][m], &blah)
> -1))
person[i].killtargetvisible = 0;
if(person[i].type==eviltype){
if(!person[i].killtargetvisible&&person[i].targetanimation==idleanim){
person[i].targetanimation=joganim;
}
if(!person[i].killtargetvisible){
person[i].aiming=0;
}
if(person[i].killtargetvisible){
person[i].onpath=0;
person[i].lastdistancevictim=200000;
person[i].pathnum=-1;
if (person[i].whichgun == nogun) {
person[i].whichgun=possiblegun[randUint(numpossibleguns)];
person[i].reloads[person[i].whichgun]=1;
if(person[i].whichgun==knife)person[i].speedmult=.8+.5*difficulty;
}
if(person[i].aiming==0)person[i].shotdelay=shotdelayamount/difficulty;
person[i].aiming=1;
if(person[i].reloading>0)person[i].aiming=0;
if(person[i].whichgun==handgun1||person[i].whichgun==handgun2)person[i].playerrotation2=-10;
if(person[i].whichgun==assaultrifle||person[i].whichgun==sniperrifle||person[i].whichgun==shotgun)person[i].playerrotation2=20;
tooclose=1300;
toofar=3000;
if(person[i].whichgun==shotgun){
tooclose=1400;
toofar=5000;
}
if(person[i].whichgun==assaultrifle){
tooclose=5000;
toofar=9000;
}
if(person[i].whichgun==sniperrifle){
tooclose=10000;
toofar=20000;
}
if(person[i].whichgun==knife){
tooclose=20;
toofar=20000;
}
if (sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) > toofar)
person[i].targetanimation=joganim;
if ((sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) <= tooclose
&& person[person[i].killtarget].skeleton.free == 0)
|| (tooclose > 200
&& sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) <= 200)
|| (tooclose <= 200
&& sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) < tooclose)) {
if(person[i].targetanimation!=idleanim){
person[i].targetanimation=idleanim;
person[i].targetframe=0;
person[i].target=0;
}
if(person[i].whichgun==knife&&person[person[i].killtarget].health==100){
murderer=i;
person[i].attacktarget=0;
person[i].attackframe=0;
if(person[person[i].killtarget].type!=zombietype)
{
auto stabpos = person[i].playercoords + flatfacing - camera.position;
playSound(gSourceID[knifeslashsound],
stabpos.x, stabpos.y, stabpos.z);
person[person[i].killtarget].bjoint1=&person[person[i].killtarget].skeleton.joints[neck];
person[person[i].killtarget].bjoint2=&person[person[i].killtarget].skeleton.joints[neck];
person[person[i].killtarget].bleeding=1;
person[person[i].killtarget].bleeddelay=1;
person[person[i].killtarget].health-=20;
person[person[i].killtarget].targetanimation=chestpainanim;
person[person[i].killtarget].targetframe=0;
person[person[i].killtarget].target=0;
person[person[i].killtarget].longdead=1;
}
}
}
finaltarget=person[person[i].killtarget].playercoords;
}
}
if(person[i].type==zombietype&&person[person[i].killtarget].health>0){
if(!person[i].killtargetvisible&&person[i].targetanimation==idleanim){
person[i].targetanimation=zombiejoganim;
}
if(!person[i].killtargetvisible){
person[i].aiming=0;
}
if(person[i].killtargetvisible){
person[i].onpath=0;
person[i].lastdistancevictim=200000;
person[i].pathnum=-1;
if(person[i].aiming==0)person[i].shotdelay=shotdelayamount/difficulty;
if (sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords) <= 20) {
murderer=i;
person[person[i].killtarget].health=0;
person[person[i].killtarget].eaten=i;
} else if (person[i].targetanimation != idleanim)
person[i].targetanimation = zombiejoganim;
finaltarget=person[person[i].killtarget].playercoords;
}
}
}
if(person[i].killtargetvisible||realcheck)person[i].pathtarget=finaltarget;
if (realcheck)
person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
}
}
if(person[i].targetanimation!=zombieeatanim||person[i].type!=zombietype){
towards = normalize(person[i].playercoords - person[i].pathtarget);
person[i].playerrotation=asin(0-towards.x)*360/6.28;
if(towards.z>0)person[i].playerrotation=180-person[i].playerrotation;
}
}
}
person[i].whichblockx=((person[i].playercoords.x+block_spacing/2)/block_spacing);
person[i].whichblocky=((person[i].playercoords.z+block_spacing/2)/block_spacing);
if (!person[i].onground) {
person[i].velocity.y += multiplier * gravity;
if (i != 0 || !visions)
person[i].playercoords += person[i].velocity * multiplier;
}
//Death by bleeding/shock
if(person[i].health<=0){
person[i].skeleton.offset=0;
person[i].skeleton.free=1;
person[i].longdead=1;
for (auto& joint : person[i].skeleton.joints) {
joint.position += joint.offset;
joint.position = rotate(joint.position, 0, person[i].playerrotation, 0);
joint.position += person[i].playercoords;
joint.realoldposition = joint.position;
joint.velocity = rotate(joint.velocity, 0, person[i].playerrotation, 0);
joint.velocity += person[i].velocity;
joint.velocity += person[i].facing * 4;
}
}
}
//Rag doll
if(person[i].skeleton.free==1&&person[i].longdead>0){
person[i].whichblockx=((person[i].skeleton.joints[0].position.x+block_spacing/2)/block_spacing);
person[i].whichblocky=((person[i].skeleton.joints[0].position.z+block_spacing/2)/block_spacing);
move = {
(float) person[i].whichblockx * block_spacing,
0.0f,
(float) person[i].whichblocky * block_spacing,
};
person[i].skeleton.DoGravity();
if(person[i].averageloc.y<=50)person[i].skeleton.DoConstraints(&blocksimplecollide[citytype[person[i].whichblockx][person[i].whichblocky]],&move,cityrotation[person[i].whichblockx][person[i].whichblocky]*90);
if(person[i].averageloc.y>50)person[i].skeleton.DoConstraints(&blockcollide[citytype[person[i].whichblockx][person[i].whichblocky]],&move,cityrotation[person[i].whichblockx][person[i].whichblocky]*90);
person[i].oldaverageloc=person[i].averageloc;
person[i].averageloc = {};
for (auto& joint : person[i].skeleton.joints)
person[i].averageloc += joint.position;
person[i].averageloc /= max_joints;
person[i].playercoords=person[i].averageloc;
if (person[i].longdead < multiplier * 0.5f
&& person[i].longdead > 0)
person[i].drawSkeleton();
if (sqrlen(person[i].averageloc - person[i].oldaverageloc) < 0.2 * multiplier)
person[i].longdead -= multiplier / 2;
}
if(person[i].skeleton.free==1&&person[i].longdead<=0&&person[i].health>0&&person[i].longdead!=-1){
person[i].longdead=1;
person[i].skeleton.free=0;
person[i].currentanimation=lyinganim;
person[i].target=0;
person[i].targetframe=0;
//Get up from front or back?
if(person[i].skeleton.forward.y>0)
person[i].targetanimation=getupfrontanim;
else
person[i].targetanimation=getupbackanim;
//Find playercoords
person[i].playercoords=person[i].averageloc;
for (auto& joint : person[i].skeleton.joints)
if (joint.position.y > person[i].playercoords.y)
person[i].playercoords.y = joint.position.y;
//Find orientation
XYZ firsttop = normalize(person[i].skeleton.joints[neck].position
- person[i].skeleton.joints[groin].position);
person[i].playerrotation=acos(0-firsttop.z);
person[i].playerrotation*=360/6.28;
if(0>firsttop.x)person[i].playerrotation=360-person[i].playerrotation;
person[i].playerrotation*=-1;
person[i].playervelocity = {};
if(person[i].targetanimation==getupfrontanim)person[i].playerrotation+=180;
for (int j = 0; j < max_joints; ++j) {
person[i].tempanimation.position[j][0]=person[i].skeleton.joints[j].position-person[i].playercoords;
person[i].tempanimation.position[j][0]=rotate(person[i].tempanimation.position[j][0],0,-person[i].playerrotation,0);
}
}
}
attackCloseRange(this, flatfacing);
if (person[0].currentanimation == diveanim && !visions)
tackle(this, flatfacing);
// Empty magazine
if (person[0].firing && person[0].ammo <= 0) {
auto soundpos = person[0].playercoords - camera.position;
playSound(gSourceID[clicksound],
soundpos.x, soundpos.y, soundpos.z);
person[0].firing = false;
}
XYZ wallhit;
XYZ start;
int numshots;
for (int j = 0; j < numpeople; j++) {
if (j && person[j].type != eviltype)
continue;
if (person[j].ammo <= 0
|| person[j].reloading > 0
|| person[j].targetanimation == joganim
|| person[j].aimamount < 1)
person[j].firing = false;
else if (j)
person[j].firing = person[j].whichgun != nogun
&& person[j].whichgun != knife
&& person[j].killtargetvisible
&& person[j].shotdelay < 0;
if (j && person[j].killtargetvisible
&& person[j].whichgun != nogun
&& person[j].whichgun != knife) {
person[j].aiming = 1;
if (person[j].shotdelay > 0)
person[j].shotdelay -= multiplier * 0.9;
}
if (person[j].skeleton.free == 1
|| person[j].targetanimation == getupfrontanim
|| person[j].targetanimation == getupbackanim)
person[j].shotdelay = shotdelayamount
/ difficulty;
if (person[j].ammo == 0
&& person[j].reloads[person[j].whichgun] > 0) {
person[j].ammo = -1;
person[j].aiming = 0;
}
if (!person[j].firing || person[j].aiming < 1 || person[j].recoil > 0)
continue;
if (j != 0 || person[j].whichgun != assaultrifle)
person[j].firing = false;
person[j].shotdelay = shotdelayamount / difficulty;
HitStruct hitstruct, temphitstruct;
person[j].litup = 1;
person[j].recoil = 1;
float olddistance = 0.0f, distance = 0.0f;
float totalarea = 0.0f;
int whichhit = -1;
numshots = (person[j].whichgun == shotgun) ? 7 : 1;
if (person[j].whichgun != grenade)
person[j].ammo--;
for (int p = 0; p < numshots; p++) {
XYZ aim;
if (j)
aim = aimBot(this, j);
else if (!this->zoom)
aim = aimPlayer(this);
else
aim = facing;
aim = normalize(aim);
int aimjoint;
switch (person[j].whichgun) {
case handgun1:
case handgun2:
aimjoint = rightwrist;
break;
default:
aimjoint = lefthand;
}
start = person[j].playercoords
+ rotate(person[j].skeleton.joints[aimjoint].position,
0, person[j].playerrotation, 0);
if (j == 0 && person[j].grenphase) {
person[j].grenphase = false;
sprites.MakeSprite(grenadesprite, 1, 1, 1, 1,
start, aim * 200, 1.01);
}
auto startsub = rotate(aim,
0, -person[j].playerrotation, 0);
startsub = rotate(startsub, 90, 0, 0);
startsub *= rotate(startsub,
0, person[j].playerrotation,0);
switch (person[j].whichgun) {
case sniperrifle:
case shotgun:
start -= startsub * 0.35f;
break;
case handgun1:
case handgun2:
start -= startsub * 0.55f;
break;
default: // assaultrifle
start -= startsub * 0.25f;
}
if (p == numshots - 1) {
auto crouch = person[j].currentanimation == crouchanim;
float rot = 0.0f, rot2 = 0.0f;
switch (person[j].whichgun) {
case sniperrifle:
case shotgun:
rot2 = crouch ? 3.0f : 7.0f;
break;
case handgun1:
rot2 = crouch ? 4.0f : 6.0f;
break;
case handgun2:
rot2 = crouch ? 3.0f : 5.0f;
break;
case assaultrifle:
rot = randInt(-100, 100) / (crouch ? 60.0f : 50.0f);
rot2 = crouch ? 1.5f : 2.3f;
break;
}
if (j == 0) { // player
camera.rotation -= rot;
camera.rotation2 -= rot2;
}
ALuint gunsound;
switch (person[j].whichgun) {
case sniperrifle:
gunsound = gSourceID[riflesound];
break;
case shotgun:
gunsound = gSourceID[shotgunsound];
break;
case handgun1:
gunsound = gSourceID[pistol1sound];
break;
case handgun2:
gunsound = gSourceID[pistol2sound];
break;
default: // assaultrifle
gunsound = gSourceID[machinegunsound];
}
auto soundpos = start - camera.position;
playSound(gunsound, soundpos.x,
soundpos.y, soundpos.z);
}
XYZ end {start + aim * 1000};
int bulletstrength=1;
int firstpass=-1;
for(int m=0;m -1) {
hitNorm = rotate(model->faces[whichtri][0].normal,
0, hitRot, 0);
goto hit_terrain;
}
}
model = &this->Bigstreet;
move = {camera.position.x, 0.0f, camera.position.z};
hitRot = 0.0f;
whichtri = segCrossModelTrans(start, end,
model, move, hitRot, &wallhit);
if (whichtri > -1) {
hitNorm = {0.0f, 1.0f, 0.0f};
hit_terrain:
whichhit = -1;
addDecal(this->decals, BULLET_HOLE, wallhit, 0.7f,
hitNorm, whichtri, model, move, hitRot);
const auto& velocity = hitNorm * 3;
switch (person[j].whichgun) {
case sniperrifle:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, wallhit, velocity, 10);
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, wallhit, velocity, 2);
break;
case shotgun:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, wallhit, velocity, 5);
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, wallhit, velocity, .8);
break;
case assaultrifle:
case handgun1:
case handgun2:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, wallhit, velocity, 6);
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, wallhit, velocity, 1);
break;
}
auto soundpos = wallhit - camera.position;
playSound(gSourceID[wallhitsound],
soundpos.x, soundpos.y, soundpos.z);
}
if (m == 0 && j == 0 && slomo == 2){
if (whichhit == -1)
alSourcePlay(gSourceID[disguisekillsound]);
alSourcef(gSourceID[whichsong], AL_PITCH, 1);
slomo = 0;
flashamount = 0.5f;
flashr = flashg = flashb = 1.0f;
}
// Impact with person
if(whichhit!=-1&&whichhit!=firstpass){
if(j==0)person[whichhit].dead=1;
if(whichhit==1){
murderer=j;
}
if(person[whichhit].health==100&&j==0){
if(person[whichhit].type==civiliantype)civkills++;
if(person[whichhit].type==eviltype)goodkills++;
}
if(person[whichhit].health==100&&j!=0){
badkills++;
}
bool penetrate = (numshots > 1) ? 0 : !randUint(3);
if (penetrate) {
bulletstrength = 2;
firstpass = whichhit;
end = start + aim * 1000;
}
if(person[j].whichgun==assaultrifle)person[whichhit].health-=20;
if(person[j].whichgun==assaultrifle&&person[whichhit].type==zombietype)person[whichhit].health-=60;
if(person[j].whichgun==handgun1){
if(person[whichhit].type!=zombietype)person[whichhit].health-=100;
if(person[whichhit].type==zombietype)person[whichhit].health-=100;
person[whichhit].DoAnimations(whichhit);
}
if(person[j].whichgun==handgun2)person[whichhit].health-=20;
if(person[j].whichgun==handgun2&&person[whichhit].type==zombietype)person[whichhit].health-=60;
if(person[j].whichgun==sniperrifle&&m!=0)person[whichhit].health-=30;
if(person[j].whichgun==shotgun)person[whichhit].health-=60;
if(person[j].whichgun==sniperrifle&&m==0){
if(person[whichhit].type!=zombietype)person[whichhit].health-=100;
if(person[whichhit].type==zombietype)person[whichhit].health-=120;
person[whichhit].DoAnimations(whichhit);
}
if(hitstruct.joint1->modelnum==headmodel&&person[whichhit].type!=zombietype){
person[whichhit].health-=60;
}
if(person[whichhit].type==zombietype)person[whichhit].speedmult-=.05;
if(person[whichhit].type==zombietype)person[whichhit].maxhealth-=10;
if (whichhit == 0) {
bulletstrength = 1;
person[0].health = 100;
flashamount = 1;
flashr = flashg = flashb = 0;
auto soundsrc = (hitstruct.hitlocation - camera.position) / soundscalefactor;
float gLoc[] {soundsrc.x, soundsrc.y, soundsrc.z};
alSourcefv(gSourceID[bodywhacksound], AL_POSITION, gLoc);
alSourcePlay(gSourceID[bodywhacksound]);
}
person[whichhit].longdead=1;
if(person[whichhit].health<=0){
person[whichhit].skeleton.offset=0;
if(person[whichhit].skeleton.free!=1){
person[whichhit].skeleton.free=1;
totalarea=0;
for (auto& joint : person[whichhit].skeleton.joints) {
joint.position = rotate(joint.position, 0, person[whichhit].playerrotation, 0);
joint.position += person[whichhit].playercoords;
joint.realoldposition = joint.position;
joint.velocity = person[whichhit].velocity;
joint.velocity.x += randInt(-4, 4);
joint.velocity.y += randInt(-4, 4);
joint.velocity.z += randInt(-4, 4);
}
}
for (auto& joint : person[whichhit].skeleton.joints) {
auto distance = sqrlen(joint.position - hitstruct.hitlocation);
if (distance < 200) {
totalarea += 200 / distance;
joint.velocity += aim * 200 / distance / totalarea * 200;
}
}
}
if(person[whichhit].health>0){
if (person[whichhit].killtargetvisible == 0
&& person[whichhit].type != zombietype
&& person[whichhit].currentanimation !=getupfrontanim
&& person[whichhit].currentanimation != getupbackanim) {
if(hitstruct.joint1->modelnum==headmodel)person[whichhit].targetanimation=headpainanim;
if(hitstruct.joint1->modelnum==chestmodel)person[whichhit].targetanimation=chestpainanim;
if(hitstruct.joint1->modelnum==abdomenmodel)person[whichhit].targetanimation=stomachpainanim;
if(hitstruct.joint1->label==rightelbow||hitstruct.joint1->label==rightshoulder||hitstruct.joint1->label==rightwrist||hitstruct.joint1->label==righthand)person[whichhit].targetanimation=rightarmpainanim;
if(hitstruct.joint1->label==leftelbow||hitstruct.joint1->label==leftshoulder||hitstruct.joint1->label==leftwrist||hitstruct.joint1->label==lefthand)person[whichhit].targetanimation=leftarmpainanim;
if(hitstruct.joint1->label==rightknee||hitstruct.joint1->label==righthip||hitstruct.joint1->label==rightankle||hitstruct.joint1->label==rightfoot)person[whichhit].targetanimation=rightlegpainanim;
if(hitstruct.joint1->label==leftknee||hitstruct.joint1->label==lefthip||hitstruct.joint1->label==leftankle||hitstruct.joint1->label==leftfoot)person[whichhit].targetanimation=leftlegpainanim;
person[whichhit].targetframe=0;
person[whichhit].target=0;
}
person[whichhit].skeleton.offset=1;
for (auto& joint : person[whichhit].skeleton.joints) {
auto distance = sqrlen(rotate(joint.position, 0, person[whichhit].playerrotation, 0)
+ person[whichhit].playercoords
- hitstruct.hitlocation);
if(distance < 200) {
totalarea += 200 / distance;
joint.offset += rotate(aim * 200 / distance / totalarea * 10,
0, -person[whichhit].playerrotation, 0);
}
if (sqrlen(joint.offset) > 36)
joint.offset = normalize(joint.offset) * 6;
}
}
if(hitstruct.joint1->modelnum==headmodel&&person[whichhit].health<=0){
for (int j = 0; j < max_joints; ++j) {
if(&person[whichhit].skeleton.joints[j]==hitstruct.joint1||&person[whichhit].skeleton.joints[j]==hitstruct.joint2){
if (j != abdomen && j != groin && j != neck) {
sprites.MakeSprite(bloodspritedown, 0.8f, 1, 0.2f, 0.2f,
person[whichhit].skeleton.joints[j].position,
person[whichhit].skeleton.joints[j].velocity / 3, 9);
for (int tmp = 0; tmp < 4; ++tmp)
sprites.MakeSprite(bloodspritedown, 0.8f, 1, 0.2f, 0.2f,
person[whichhit].skeleton.joints[j].position,
rotate(person[whichhit].skeleton.joints[j].velocity / 3,
randUint(360), randUint(360), 0) / 5, 5);
person[whichhit].skeleton.DeleteJoint(j);
person[whichhit].skeleton.broken=1;
person[whichhit].health=-10000;
person[whichhit].skeleton.joints[j].existing=0;
if(person[whichhit].type==zombietype)score+=300;
}
}
}
}
XYZ velocity;
velocity=aim*-8;
//blood
if (hitstruct.joint1->modelnum != headmodel) {
if(person[j].whichgun==sniperrifle)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*0, 5);
if(person[j].whichgun==sniperrifle&&penetrate)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*-3, 7);
if(person[j].whichgun==shotgun)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*0, 5);
if(person[j].whichgun==shotgun&&penetrate)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*-3, 7);
if(person[j].whichgun==assaultrifle)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*0, 3);
if(person[j].whichgun==assaultrifle&&penetrate)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*-3, 7);
if(person[j].whichgun==handgun1)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*0, 3);
if(person[j].whichgun==handgun1&&penetrate)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*-3, 4);
if(person[j].whichgun==handgun2)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*0, 3);
if(person[j].whichgun==handgun2&&penetrate)sprites.MakeSprite(bloodspritenoup, 1, 1, 0, 0, hitstruct.hitlocation, velocity*-3, 4);
}else{
sprites.MakeSprite(bloodspritenoup, 1, 1, .2, .2, hitstruct.hitlocation, velocity*0, 6);
sprites.MakeSprite(bloodspritenoup, 1, 1, .5, .5, hitstruct.hitlocation, velocity*-2, 7);
sprites.MakeSprite(bloodspritenoup, 1, 1, .2, .2, hitstruct.hitlocation, velocity*-3, 10);
}
person[whichhit].bjoint1=hitstruct.joint1;
person[whichhit].bjoint2=hitstruct.joint2;
person[whichhit].bleeding=1;
person[whichhit].bleeddelay=1;
auto hitsound = (hitstruct.joint1->modelnum == headmodel)
? gSourceID[headshotsound]
: gSourceID[bodyhitsound];
auto hitpos = hitstruct.hitlocation
- camera.position;
playSound(hitsound, hitpos.x, hitpos.y, hitpos.z);
}
lastshot[0]=start;
lastshot[1]=end;
auto velocity = aim * 8;
if(person[j].whichgun!=sniperrifle&&person[j].whichgun!=shotgun&&p==numshots-1)sprites.MakeSprite(smokesprite, .3, 1, 1, 1, start+aim*1.5, velocity, 3);
if(person[j].whichgun==shotgun&&p==numshots-1)sprites.MakeSprite(smokesprite, .4, 1, 1, 1, start+aim*1.5, velocity, 5);
if (person[j].whichgun == sniperrifle && !this->zoom)
sprites.MakeSprite(smokesprite, 0.3f, 1, 1, 1.0f, start + aim * 2.2f, velocity, 4.0f);
if (j != 0 || !this->zoom)
sprites.MakeSprite(bullet, 0.07f, 1, 1, 0.7f, lastshot[0] +aim, lastshot[1], 0.2f);
// Nearby bullet whoosh
XYZ* a = &lastshot[0];
*a += aim;
XYZ* b = &lastshot[1];
XYZ* c = &camera.position;
XYZ nearest {};
long dot_ta = (c->x - a->x) * (b->x - a->x)
+ (c->y - a->y) * (b->y - a->y)
+ (c->z - a->z) * (b->z - a->z);
long dot_tb = (c->x - b->x) * (a->x - b->x)
+ (c->y - b->y) * (a->y - b->y)
+ (c->z - b->z) * (a->z - b->z);
if (dot_ta > 0 && dot_tb > 0) {
nearest.x = a->x + ((b->x - a->x) * dot_ta) / (dot_ta + dot_tb);
nearest.y = a->y + ((b->y - a->y) * dot_ta) / (dot_ta + dot_tb);
nearest.z = a->z + ((b->z - a->z) * dot_ta) / (dot_ta + dot_tb);
}
if (nearest.x
&& sqrlen(nearest - camera.position) < 10
&& (thirdperson == 2 || j != 0)) {
auto nearsound = nearest - camera.position;
playSound(gSourceID[nearbulletsound],
nearsound.x, nearsound.y, nearsound.z);
}
}
}
}
if (thirdperson && !this->zoom)
renderLaser(this);
//Snow
snowdelay-=multiplier;
while(snowdelay<0&&environment==snowy_environment){
snowdelay+=1/precipitationdensity*2;
XYZ velocity {0.0f, -5.0f, 0.0f};
start=camera.position;
start.y+=precipitationvert;
start.x += randUint(precipitationhorz);
start.z += randUint(precipitationhorz);
sprites.MakeSprite(snowsprite, 1, 1, 1, 1, start, velocity, 1.01);
}
while(snowdelay<0&&environment==rainy_environment){
snowdelay+=1/precipitationdensity/4;
XYZ velocity {0.0f, -100.0f, 0.0f};
start=camera.position;
start.y+=precipitationvert;
start.x += randUint(precipitationhorz) * 0.5f;
start.z += randUint(precipitationhorz) * 0.5f;
sprites.MakeSprite(rainsprite, .5, 1, 1, 1, start, velocity, 2.00);
}
// Grenade collision
for (int i = 0; i < sprites.howmanysprites; ++i) {
switch (sprites.type[i]) {
case grenadesprite:
sprites.brightness[i] -= multiplier * 0.3f;
break;
case pinsprite:
case spoonsprite:
sprites.brightness[i] -= multiplier * 0.2f;
break;
default:
continue;
}
bool impact = false;
if (sqrlen(sprites.velocity[i]) > 0) {
int wherex = sprites.location[i].x / block_spacing + 0.5f;
int wherey = sprites.location[i].z / block_spacing + 0.5f;
move = {(float) wherex * block_spacing, 0.0f, (float) wherey * block_spacing};
whichtri = segCrossModelTrans(sprites.oldlocation[i],
sprites.location[i],
blocks + citytype[wherex][wherey],
move, cityrotation[wherex][wherey] * 90,
&wallhit);
if (whichtri != -1) {
impact = true;
const auto& normalrotated = rotate(blocks[citytype[wherex][wherey]].faces[whichtri][0].normal,
0, cityrotation[wherex][wherey] * 90, 0);
if (sprites.size[i] > 1)
addDecal(this->decals, CRATER, wallhit, 9.0f,
normalrotated, whichtri,
&blocks[citytype[wherex][wherey]],
move, cityrotation[wherex][wherey] * 90);
sprites.location[i] = wallhit + normalrotated * 0.02f;
reflect(&sprites.velocity[i], normalrotated);
sprites.velocity[i] *= 0.3f;
if (sprites.type[i] == grenadesprite && sprites.size[i] <= 1) {
auto soundpos = sprites.location[i] - camera.position;
auto v = sqrlen(sprites.velocity[i]) * 0.2f;
ALfloat gLoc[] {
soundpos.x / v / soundscalefactor,
soundpos.y / v / soundscalefactor,
soundpos.z / v / soundscalefactor,
};
int whichsound = bouncesound + randUint(2);
alSourcefv(gSourceID[whichsound], AL_POSITION, gLoc);
alSourcePlay(gSourceID[whichsound]);
}
if (sqrlen(sprites.velocity[i]) <= 10)
sprites.velocity[i] = {};
}
if(sprites.location[i].y<0){
impact = true;
sprites.velocity[i].y *= -1.0f;
sprites.velocity[i] *= 0.3f;
sprites.location[i].y = 0.0f;
if(sprites.type[i]==grenadesprite){
if(sprites.size[i]>1){
move = {};
sprites.location[i].y=-.5;
XYZ normish = {0.0f, 1.0f, 0.0f};
addDecal(this->decals, CRATER, sprites.location[i], 9.0f,
normish, 0, blocks + citytype[wherex][wherey], move, 0);
}
auto soundpos = sprites.location[i] - camera.position;
auto v = sqrlen(sprites.velocity[i]) * 0.2f;
ALfloat gLoc[] {
soundpos.x / v / soundscalefactor,
soundpos.y / v / soundscalefactor,
soundpos.z / v / soundscalefactor,
};
int whichsound = bouncesound + randUint(2);
alSourcefv(gSourceID[whichsound], AL_POSITION, gLoc);
if (sprites.size[i] <= 1)
alSourcePlay(gSourceID[whichsound]);
}
if (sqrlen(sprites.velocity[i]) <= 10)
sprites.velocity[i] = {};
}
if (sprites.type[i] == grenadesprite
&& sqrlen(sprites.velocity[i]) > 20) {
for (int j = 0; j < numpeople; ++j) {
if ((j == 0 && sprites.brightness[i] > 0.9f)
|| !person[j].existing)
continue;
auto hitstruct = person[j].BulletCollideWithPlayer(j, sprites.oldlocation[i], sprites.location[i]);
if (!hitstruct.collision)
continue;
impact = true;
sprites.location[i] = hitstruct.hitlocation;
auto landpos = sprites.location[i] - camera.position;
auto v = sqrlen(sprites.velocity[i]) * 0.2f;
ALfloat gLoc[] {
landpos.x / v / soundscalefactor,
landpos.y / v / soundscalefactor,
landpos.z / v / soundscalefactor,
};
sprites.velocity[i] *= -0.3f;
if (person[j].skeleton.free)
continue;
if((hitstruct.joint1->label==head||hitstruct.joint2->label==head)&&person[j].type!=zombietype){
alSourcefv(gSourceID[headwhacksound], AL_POSITION, gLoc);
if(sprites.size[i]<=1)alSourcePlay(gSourceID[headwhacksound]);
person[j].skeleton.free=1;
person[j].longdead=1;
for (auto& joint : person[j].skeleton.joints) {
joint.realoldposition = joint.position
= rotate(joint.position,
0, person[j].playerrotation, 0)
+ person[j].playercoords;
joint.velocity = person[j].velocity;
joint.velocity.x += randInt(-4, 4);
joint.velocity.y += randInt(-4, 4);
joint.velocity.z += randInt(-4, 4);
}
hitstruct.joint1->velocity += sprites.velocity[i];
hitstruct.joint2->velocity += sprites.velocity[i];
if (person[j].type == civiliantype)
civkills++;
if (person[j].type == eviltype)
goodkills++;
} else {
float totalarea = 0.0f;
alSourcefv(gSourceID[bodywhacksound], AL_POSITION, gLoc);
if(sprites.size[i]<=1)alSourcePlay(gSourceID[bodywhacksound]);
person[j].skeleton.offset=1;
for (auto& joint : person[j].skeleton.joints) {
auto distance = sqrlen(rotate(joint.position, 0, person[j].playerrotation, 0)
+ person[j].playercoords
- hitstruct.hitlocation);
if (distance < 200) {
totalarea += 200 / distance;
joint.offset += rotate(sprites.velocity[i] * 0.1 * 200 / distance / totalarea * 10, 0, -person[j].playerrotation, 0);
}
if (sqrlen(joint.offset) > 9) {
joint.offset = normalize(joint.offset) * 3;
}
}
}
}
}
sprites.oldlocation[i]=sprites.location[i];
}
// Explode
if (sprites.type[i] == grenadesprite
&& (sprites.brightness[i] <= 0 || (impact && sprites.size[i] > 1))) {
sprites.brightness[i] = 0;
sprites.MakeSprite(smokesprite, 1, 1, 1, 1, sprites.location[i], facing*0, 60);
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, sprites.location[i], facing*0, 9);
auto explodepos = sprites.location[i] - camera.position;
ALfloat gLoc[] {
explodepos.x / 3 / soundscalefactor,
explodepos.y / 3 / soundscalefactor,
explodepos.z / 3 / soundscalefactor,
};
alSourcefv(gSourceID[explosionsound], AL_POSITION, gLoc);
alSourcePlay(gSourceID[explosionsound]);
XYZ relation;
camerashake = 1 - len(person[0].playercoords - sprites.location[i]) / 200;
overpoint=sprites.location[i];
overpoint.y+=3000;
underpoint=sprites.location[i];
underpoint.y-=3000;
int wherex=(sprites.location[i].x+block_spacing/2)/block_spacing;
int wherey=(sprites.location[i].z+block_spacing/2)/block_spacing;
move = {(float) wherex * block_spacing, 0.0f, (float) wherey * block_spacing};
XYZ temp;
whichtri = segCrossModelTrans(overpoint, underpoint,
&sidewalkcollide,
move, cityrotation[wherex][wherey] * 90, &temp);
XYZ normish = {0.0f, 1.0f, 0.0f};
if (whichtri > -1) {
addDecal(this->decals, CRATER, sprites.location[i], 9.0f,
normish, 0, &sidewalkcollide, move,
cityrotation[wherex][wherey]*90);
} else {
temp = sprites.location[i];
temp.y = -0.5f;
move = {0.0f};
addDecal(this->decals, CRATER, sprites.location[i], 9.0f,
normish, 0, &sidewalkcollide, move, 0);
}
for(int k=0;k 700
|| person[k].skeleton.free)
&& (sqrlen(person[k].averageloc - sprites.location[i]) > 700
|| !person[k].skeleton.free)))
continue;
if(person[k].skeleton.free!=1){
if(person[k].type==civiliantype)civkills++;
if(person[k].type==eviltype)goodkills++;
person[k].skeleton.free=1;
person[k].killtargetvisible=0;
if ((sqrlen(person[k].playercoords - sprites.location[i]) < 600
&& person[k].skeleton.free < 1)
|| (sqrlen(person[k].averageloc - sprites.location[i]) < 600
&& person[k].skeleton.free >= 1)
|| person[k].type == playertype) {
person[k].health-=100;
person[k].bleeding=1;
}
person[k].DoAnimations(k);
person[k].longdead = 1;
person[k].bleeddelay = 1;
person[k].bjoint1 = &person[k].skeleton.joints[head];
person[k].bjoint2 = &person[k].skeleton.joints[neck];
for (auto& joint : person[k].skeleton.joints) {
joint.position = rotate(joint.position, 0, person[k].playerrotation, 0);
joint.position += person[k].playercoords;
joint.realoldposition = joint.position;
joint.velocity = rotate(joint.velocity, 0, person[k].playerrotation, 0);
joint.velocity += person[k].velocity;
joint.velocity.x += randInt(-9, 9);
joint.velocity.y += randInt(-9, 9);
joint.velocity.z += randInt(-9, 9);
}
}
person[k].longdead=1;
for (auto& joint : person[k].skeleton.joints) {
relation = normalize(joint.position - sprites.location[i]);
auto distance = len(joint.position - sprites.location[i]);
if (distance > 1)
joint.velocity += relation / distance * 300;
else
joint.velocity += relation * 300;
// Sever stuff
if (sqrlen(joint.velocity) > 1500
&& joint.existing && randUint(5)) {
sprites.MakeSprite(bloodspritedown, 0.8, 1, 0.2, 0.2, joint.position, joint.velocity / 3, 9);
person[k].skeleton.DeleteJoint(&joint
- person[k].skeleton.joints);
person[k].skeleton.broken=2;
person[k].health=-10000;
joint.existing = false;
}
}
}
}
}
//camera shake
camerashake-=multiplier;
if(camerashake<0)camerashake=0;
// Camera position and angle
const auto& average = this->zoom
? this->person[0].skeleton.joints[righthand].position
: (this->person[0].skeleton.joints[head].position
* (0.5f + this->person[0].aimamount * 0.5f))
+ (this->person[0].skeleton.joints[neck].position
* (0.5f - this->person[0].aimamount * 0.5f));
if (thirdperson != 2) {
camera.position = person[0].skeleton.free ? average
: person[0].playercoords
+ rotate(average, 0, person[0].playerrotation, 0);
if (camera.position.y < 0.1f)
camera.position.y = 0.1f;
if (this->zoom || visions || this->person[0].aimamount <= 0
|| this->person[0].whichgun == nogun
|| this->person[0].whichgun == grenade
|| this->person[0].whichgun == knife) {
camera.visrotation = camera.rotation;
camera.visrotation2 = camera.rotation2;
} else {
camera.visrotation = std::min(camera.rotation + 7,
std::max(camera.rotation - 7, camera.visrotation));
camera.visrotation2 = std::min(camera.rotation2 + 15,
std::max(camera.rotation2 - 15, camera.visrotation2));
}
}
//Kill count
for(int i=0;i0&&person[i].health<=0){
if(i==1)alSourcePlay(gSourceID[losesound]);
if(person[i].type==civiliantype){
alSourcePlay(gSourceID[disguisekillsound]);
score-=300;
}
if(person[i].type==eviltype){
alSourcePlay(gSourceID[soulinsound]);
score+=75;
if(person[i].whichgun==knife)score+=50;
}
person[i].firstlongdead=0;
}
person[i].oldhealth=person[i].health;
}
if (slomo == 2) {
psychicpower -= multiplier*15;
if (psychicpower < 0) {
alSourcef(gSourceID[whichsong], AL_PITCH, 1);
slomo = 0;
alSourcePlay(gSourceID[soulinsound]);
psychicpower = 0;
flashamount = 0.5f;
flashr = flashg = flashb = 1.0f;
}
}
psychicpower+=multiplier*5;
if(psychicpower>10)psychicpower=10;
setListener(this, facing);
if (score < 0)
score = 0;
}