// 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; }