AssaultCube Reloaded Wiki
Explore
Main Page
All Pages
Community
Interactive Maps
Recent Blog Posts
Wiki Content
Recently Changed Pages
AssaultCube Reloaded Wiki
Douze
Weapons
Linux Support
Compiling
Mods
Server Guide
Source code
Rendergl.cpp
Server.h
Ac bot.cpp
Ac bot.h
Ac bot ai.cpp
Bot.cpp
Bot.h
Maps
Official maps
Cube maps
Official maps
Acr mini arena
Ac metl2
Ac desert4
Ext ac desert2
Community
Recent blog posts
Forum
FANDOM
Fan Central
BETA
Games
Anime
Movies
TV
Video
Wikis
Explore Wikis
Community Central
Start a Wiki
Don't have an account?
Register
Sign In
Sign In
Register
AssaultCube Reloaded Wiki
153
pages
Explore
Main Page
All Pages
Community
Interactive Maps
Recent Blog Posts
Wiki Content
Recently Changed Pages
AssaultCube Reloaded Wiki
Douze
Weapons
Linux Support
Compiling
Mods
Server Guide
Source code
Rendergl.cpp
Server.h
Ac bot.cpp
Ac bot.h
Ac bot ai.cpp
Bot.cpp
Bot.h
Maps
Official maps
Cube maps
Official maps
Acr mini arena
Ac metl2
Ac desert4
Ext ac desert2
Community
Recent blog posts
Forum
Editing
Serverballistics.h
Back to page
Edit
Edit source
View history
Talk (0)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
<source lang="cpp"> #include "ballistics.h" float srayclip(const vec &o, const vec &ray, vec *surface = NULL) { float dist = sraycube(o, ray, surface); vec to = ray; to.mul(dist).add(o); bool collided = false; vec end; loopv(sents) { if (sents[i].type != CLIP /*&& sents[i] != MAPMODEL*/) continue; entity &e = sents[i]; // attr1, attr2, attr3, attr4 // elevation, xrad, yrad, height if (intersectbox(vec(e.x, e.y, getblockfloor(getmaplayoutid(e.x, e.y)) + e.attr1 + e.attr4 / 2), vec(max(0.1f, (float)e.attr2), max(0.1f, (float)e.attr3), max(0.1f, e.attr4 / 2.f)), o, to, &end)) { to = end; collided = true; if (surface) { *surface = vec(0, 0, 0); // which surface did it hit? } } } return collided ? to.dist(o) : dist; } // trace a shot void straceShot(const vec &from, vec &to, vec *surface = NULL) { vec tracer(to); tracer.sub(from).normalize(); const float dist = srayclip(from, tracer, surface); to = tracer.mul(dist - .1f).add(from); } // normal shots (ray through sphere and cylinder check) static inline int hitplayer(const vec &from, float yaw, float pitch, const vec &to, const vec &target, const vec &head, vec *end = NULL) { float dist; // intersect head if (!head.iszero() && intersectsphere(from, to, head, HEADSIZE, dist)) { if (end) (*end = to).sub(from).mul(dist).add(from); return HIT_HEAD; } float y = yaw*RAD, p = (pitch / 4 + 90)*RAD, c = cosf(p); vec bottom(target), top(sinf(y)*c, -cosf(y)*c, sinf(p)); bottom.z -= PLAYERHEIGHT; top.mul(PLAYERHEIGHT/* + d->aboveeye*/).add(bottom); // space above shoulders removed // torso bottom.sub(top).mul(TORSOPART).add(top); if (intersectcylinder(from, to, bottom, top, PLAYERRADIUS, dist)) { if (end) (*end = to).sub(from).mul(dist).add(from); return HIT_TORSO; } // restore to body bottom.sub(top).div(TORSOPART).add(top); // legs top.sub(bottom).mul(LEGPART).add(bottom); if (intersectcylinder(from, to, bottom, top, PLAYERRADIUS, dist)){ if (end) (*end = to).sub(from).mul(dist).add(from); return HIT_LEG; } return HIT_NONE; } // apply spread void applyspread(const vec &from, vec &to, int spread, float factor){ if (spread <= 1) return; #define RNDD (rnd(spread)-spread/2.f)*factor vec r(RNDD, RNDD, RNDD); #undef RNDD to.add(r); } // check for critical bool checkcrit(float dist, float m, int base = 0, int low = 4, int high = 100) { return !m_real(gamemode, mutators) && !rnd((base + clamp(int(ceil(dist) * m), low, high)) * (m_classic(gamemode, mutators) ? 2 : 1)); } // easy to send shot damage messages inline void sendhit(client &actor, int gun, const vec &o, int dmg) { // no blood or explosions if using moon jump #if (SERVER_BUILTIN_MOD & 6) == 6 // 2 | 4 #if !(SERVER_BUILTIN_MOD & 4) if (m_gib(gamemode, mutators)) #endif return; #endif sendf(NULL, 1, "ri7", SV_EXPLODE, actor.clientnum, gun, dmg, (int)(o.x*DMF), (int)(o.y*DMF), (int)(o.z*DMF)); } inline void sendheadshot(const vec &from, const vec &to, int damage) { sendf(NULL, 1, "ri8", SV_HEADSHOT, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), damage); } void parsepos(client &c, const vector<posinfo> &pos, vec &out_o, vec &out_head) { const posinfo *info = NULL; loopv(pos) if (pos[i].cn == c.clientnum) { info = &pos[i]; break; } // position if (scl.lagtrust >= 2 && info) out_o = info->o; else out_o = c.state.o; // don't trust the client's position, or not provided // fix z out_o.z += PLAYERHEIGHT * c.state.crouchfactor(gamemillis); // head delta if (scl.lagtrust >= 1 && info && info->head.x > 0 && info->head.y > 0 && info->head.z > 0) { out_head = info->head; // sanity check (no insane headshot OPK) out_head.sub(out_o); if (out_head.magnitude() > 2) out_head.normalize().mul(2); // the center of our head cannot deviate from our neck more than 50 cm out_head.add(out_o); } // no match? not trusted? approximate a location for the head else out_head = vec(0, -.25f, .25f).rotate_around_z(c.y * RAD).add(out_o); } // explosions // order the explosion hits by distance struct explosivehit { client *target, *owner; int damage, flags; float dist; vec o; static int compare(explosivehit *a, explosivehit *b) { // if there is more damage, the distance is closer, therefore move it up: (-a) - (-b) = b - a return b->damage - a->damage; } }; // explosions call this to check int radialeffect(client &owner, client &target, vector<explosivehit> &hits, const vec &o, int weap, bool gib, bool max_damage = false) { vec hit_location = target.state.o, hit_location2 = target.state.o; hit_location.z += (PLAYERHEIGHT + PLAYERABOVEEYE) / 2.f; hit_location2.z += PLAYERHEIGHT * target.state.crouchfactor(gamemillis); // distance calculations float dist = max_damage ? 0 : min(hit_location.dist(o), hit_location2.dist(o)); const bool useReciprocal = !m_classic(gamemode, mutators); if (dist >= (useReciprocal ? guns[weap].endrange : guns[weap].rangesub)) return 0; // too far away vec ray1(hit_location), ray2(hit_location2); ray1.sub(o).normalize(); ray2.sub(o).normalize(); if (srayclip(o, ray1) < dist && srayclip(o, ray2) < dist) return 0; // not visible float dmg = effectiveDamage(weap, dist, true, useReciprocal); int expflags = gib ? FRAG_GIB : FRAG_NONE; // check for critical if (checkcrit(dist, 2.5f)) // 1 : clamp(10 * meter, 4, 100) chance { expflags |= FRAG_CRIT; dmg *= 1.4f; } // did the nade headshot? if (weap == GUN_GRENADE && &owner != &target && o.z > hit_location2.z) { expflags |= FRAG_FLAG; sendheadshot(o, (hit_location = hit_location2), dmg); dmg *= 1.2f; } // was the RPG direct? else if (weap == GUN_RPG && max_damage) expflags |= FRAG_FLAG; explosivehit &hit = hits.add(); hit.damage = (int)dmg; hit.flags = expflags; hit.target = ⌖ hit.owner = &owner; hit.dist = dist; hit.o = hit_location; return hit.damage; } // explosion call int explosion(client &owner, const vec &o2, int weap, bool teamcheck, bool gib, client *cflag) { int damagedealt = 0; vec o(o2); checkpos(o); sendhit(owner, weap, o, 0); // 0 means display explosion // these are our hits vector<explosivehit> hits; client *own_alt = NULL; // give credits to the shooter for killing the zombie! if (m_zombie(gamemode) && owner.team == TEAM_CLA && owner.state.revengelog.length() && valid_client(owner.state.revengelog.last())) { own_alt = clients[owner.state.revengelog.last()]; if (own_alt->team != TEAM_RVSF) own_alt = NULL; } // suicide bomber's killer else if (weap == GUN_GRENADE && cflag && !isteam(cflag, &owner)) own_alt = cflag; // find the hits loopv(clients) { client &target = *clients[i]; if (target.type == ST_EMPTY || target.state.state != CS_ALIVE || target.state.protect(gamemillis, gamemode, mutators)) continue; client *own = &owner; if (&owner != &target && isteam(&owner, &target)) { if (own_alt) own = own_alt; else if (teamcheck) continue; } damagedealt += radialeffect(*own, target, hits, o, weap, gib, (weap == GUN_RPG && clients[i] == cflag)); } if (m_overload(gamemode) && team_isactive(owner.team)) { const int ot = team_opposite(owner.team); sflaginfo &f = sflaginfos[ot]; vec flag_o(f.x, f.y, getsblock(getmaplayoutid((int)f.x, (int)f.y)).floor + (PLAYERHEIGHT + PLAYERABOVEEYE) / 2); const float dist = o.dist(flag_o); if (dist < PLAYERRADIUS * 4) { const int explosivedamage = guns[weap].damage >> 1; // half damagedealt += explosivedamage; sendf(NULL, 1, "ri2", SV_DAMAGEOBJECTIVE, owner.clientnum); f.damagetime = gamemillis; if ((f.damage += explosivedamage * (m_gsp1(gamemode, mutators) ? 16 : 8)) >= 255000) { f.damage = 0; flagaction(ot, FA_SCORE, owner.clientnum); } } } // sort the hits hits.sort(explosivehit::compare); // apply the hits loopv(hits) { sendhit(owner, weap, hits[i].o, hits[i].damage); serverdamage(*hits[i].target, *hits[i].owner, hits[i].damage, weap, hits[i].flags, o, hits[i].dist); } return damagedealt; } // order the nuke hits by distance struct nukehit { client *target; float distance; static int compare(nukehit *a, nukehit *b) { // less distance, so do it earlier if (a->distance < b->distance) return -1; // more distance, so do it later if (a->distance > b->distance) return 1; // same return 0; } }; void nuke(client &owner, bool suicide, bool forced_all, bool friendly_fire) { vector<nukehit> hits; loopvj(clients) { client *cl = clients[j]; if (cl->type != ST_EMPTY && cl->team != TEAM_SPECT && cl != &owner && (friendly_fire || !isteam(cl, &owner)) && (forced_all || cl->state.state == CS_ALIVE)) { // sort hits nukehit &hit = hits.add(); hit.distance = cl->state.o.dist(owner.state.o); if (cl->type == ST_AI) hit.distance += 25 * CUBES_PER_METER; // to prioritize non-bots hit.target = cl; } } hits.sort(nukehit::compare); loopv(hits) { serverdied(*hits[i].target, owner, 0, OBIT_NUKE, !rnd(3) ? FRAG_GIB : FRAG_NONE, owner.state.o, hits[i].distance); // fx sendhit(owner, GUN_GRENADE, hits[i].target->state.o, 0); } // save the best for last! if (suicide) { owner.suicide(OBIT_NUKE, FRAG_NONE); // fx sendhit(owner, GUN_GRENADE, owner.state.o, 0); } } // Hitscans struct shothit { client *target; int damage, flags; float dist; }; // hit checks client *nearesthit(client &actor, const vec &from, const vec &to, bool teamcheck, int &hitzone, const vector<posinfo> &pos, vector<int> &exclude, vec &end, bool melee = false) { client *result = NULL; float dist = 8e36f; // 2 undecillion meters #define MELEE_PRECISION 11 vec melees[MELEE_PRECISION]; if (melee) { loopi(MELEE_PRECISION) { melees[i] = to; melees[i].sub(from); /* const float angle = ((i + 1.f) / MELEE_PRECISION - 0.5f) * 85.f * RAD; // from -85 to 85 melees[i].rotate_around_x(angle * sinf(owner->aim[0])); melees[i].rotate_around_x(angle * cosf(owner->aim[0])); */ melees[i].rotate_around_z(((i + 1.f) / MELEE_PRECISION - 0.5f) * 25.f * RAD); // from 25 to 25 (50 degrees) melees[i].add(from); } } loopv(clients) { client &t = *clients[i]; clientstate &ts = t.state; // basic checks if (t.type == ST_EMPTY || ts.state != CS_ALIVE || exclude.find(i) >= 0 || (teamcheck && &actor != &t && isteam(&actor, &t)) || ts.protect(gamemillis, gamemode, mutators)) continue; const float d = ts.o.dist(from); if (d > dist) continue; vec o, head; parsepos(t, pos, o, head); int hz = HIT_NONE; if (melee) { loopi(MELEE_PRECISION) { hz = hitplayer(from, actor.y, actor.p, melees[i], o, head, &end); if (hz) continue; // one of the knife rays hit } if (!hz) continue; // none of the knife rays hit } else { hz = hitplayer(from, actor.y, actor.p, to, o, head, &end); if (!hz) continue; // no hit } result = &t; dist = d; hitzone = hz; } return result; } // do a single line int shot(client &owner, const vec &from, vec &to, const vector<posinfo> &pos, int weap, int style, const vec &surface, vector<int> &exclude, float dist = 0, float penaltydist = 0, vector<shothit> *save = NULL) { const mul &mulset = muls[guns[weap].mulset]; int hitzone = HIT_NONE; vec end = to; // out of range? if (melee_weap(weap)) { #if (SERVER_BUILTIN_MOD & 32) // super knife if (m_gib(gamemode, mutators)) { static const int lulz[3] = { GUN_SNIPER, GUN_HEAL, GUN_RPG }; sendf(NULL, 1, "ri9", SV_RICOCHET, owner.clientnum, lulz[rnd(3)], (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF)); } else #endif { // limit melee distance to.sub(from); if (to.magnitude() > guns[weap].endrange) { to.normalize().mul(guns[weap].endrange); } to.add(from); } } // calculate the hit client *hit = nearesthit(owner, from, to, !m_real(gamemode, mutators), hitzone, pos, exclude, end, melee_weap(weap) && (!(SERVER_BUILTIN_MOD & 1) || !m_gib(gamemode, mutators))); // damage check const float dist2 = dist + end.dist(from); int damage = effectiveDamage(weap, dist2 + penaltydist); #if (SERVER_BUILTIN_MOD & 16) if (!dist) { // removed: red RPG shotline explosion(owner, end, GUN_RPG, !m_real(gamemode, mutators), false); } #endif // we hit somebody if (hit && damage) { // damage multipliers if(guns[weap].mulset == MUL_PRO) { if (hitzone == HIT_HEAD) //damage *= mulset.head; ; // multiplying by 1 does nothing else damage = 0; } else if (!m_classic(gamemode, mutators) || hitzone >= HIT_HEAD || guns[weap].mulset == MUL_PRO2) { if (hitzone == HIT_HEAD) damage *= m_progressive(gamemode, mutators) ? 7 : mulset.head; else if (hitzone == HIT_TORSO) damage *= mulset.torso; // legs is always 1 } // gib check if ((melee_weap(weap) || hitzone == HIT_HEAD) && !save) style |= FRAG_GIB; // critical shots if (checkcrit(dist2, 3.5f)) // 1 in clamp(14 * meter, 4, 100) { style |= FRAG_CRIT; damage *= 1.5f; } // melee weapons (bleed/check for self) if (melee_weap(weap)) { if (hitzone == HIT_HEAD) style |= FRAG_FLAG; if (&owner == hit) return 0; // not possible else if (!isteam(&owner, hit)) // do not cause teammates to bleed { hit->state.addwound(owner.clientnum, end); sendf(NULL, 1, "ri2", SV_BLEED, hit->clientnum); } } // send bloody headshot hits... if (hitzone == HIT_HEAD) sendheadshot(from, end, damage); // send the real hit (blood fx) sendhit(owner, weap, end, damage); // apply damage if (save) { // save damage for shotgun rays shothit &h = save->add(); h.target = hit; h.damage = damage; h.flags = style; h.dist = dist2; } else serverdamage(*hit, owner, damage, weap, style, from, dist2); // add hit to the exclude list exclude.add(hit->clientnum); // penetration: distort ray and continue through... vec dir(to = end), newsurface; // 5 degrees (both ways = 10 degrees) distortion on all axis dir.sub(from) .normalize() .rotate_around_x((rnd(11) - 5)*RAD) .rotate_around_y((rnd(11) - 5)*RAD) .rotate_around_z((rnd(11) - 5)*RAD) .add(end); // retrace straceShot(end, dir, &newsurface); const int penetratedamage = shot(owner, end, dir, pos, weap, style|FRAG_PENETRATE, newsurface, exclude, dist2, penaltydist + 10 * CUBES_PER_METER, save); // distance penalty for penetrating the player sendf(NULL, 1, "ri9", SV_RICOCHET, owner.clientnum, weap, (int)(end.x*DMF), (int)(end.y*DMF), (int)(end.z*DMF), (int)(dir.x*DMF), (int)(dir.y*DMF), (int)(dir.z*DMF)); return damage + penetratedamage; } else { if (m_overload(gamemode) && team_isactive(owner.team)) { const int ot = team_opposite(owner.team); sflaginfo &f = sflaginfos[ot]; vec bottom(f.x, f.y, getsblock(getmaplayoutid((int)f.x, (int)f.y)).floor), top(bottom); top.z += PLAYERHEIGHT + PLAYERABOVEEYE; float dist; if (intersectcylinder(from, to, bottom, top, PLAYERRADIUS * 2, dist)) { damage = effectiveDamage(weap, dist + penaltydist); to.sub(from).mul(dist).add(from); sendf(NULL, 1, "ri2", SV_DAMAGEOBJECTIVE, owner.clientnum); f.damagetime = gamemillis; if ((f.damage += damage * (m_gsp1(gamemode, mutators) ? 16 : 8)) >= 255000) { f.damage = 0; flagaction(ot, FA_SCORE, owner.clientnum); } return damage; } } // ricochet if (!dist && surface.magnitude()) // ricochet once if it came from the gun directly { // reset exclusion to the owner, so a penetrated player can be hit twice if (exclude.length() > 1) exclude.setsize(1); vec dir(to); // calculate reflected ray from incident ray and surface normal dir.sub(from).normalize(); const float dotproduct = dir.dot(surface); if (fabs(dotproduct) > 0.96592582628f) // minimum angle is 15 degrees from normal return damage; // r = i - 2 (i . n) n dir.sub(vec(surface).mul(2 * dotproduct)); // 2 degrees (both ways = 4 degrees) distortion on all axis dir .rotate_around_x((rnd(5) - 2)*RAD) .rotate_around_y((rnd(5) - 2)*RAD) .rotate_around_z((rnd(5) - 2)*RAD) .add(to); // retrace vec newsurface; straceShot(to, dir, &newsurface); const int ricochetdamage = shot(owner, to, dir, pos, weap, style|FRAG_RICOCHET, newsurface, exclude, dist2, penaltydist + 15 * CUBES_PER_METER, save); // distance penalty for ricochet sendf(NULL, 1, "ri9", SV_RICOCHET, owner.clientnum, weap, (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), (int)(dir.x*DMF), (int)(dir.y*DMF), (int)(dir.z*DMF)); return damage + ricochetdamage; } } return 0; } int shotgun(client &owner, const vec &from, vector<posinfo> &pos, int weap) { int damagedealt = 0; clientstate &gs = owner.state; // many rays many hits, but we want each client to get all the damage at once... static vector<shothit> hits; hits.setsize(0); loopi(SGRAYS) { // check rays and sum damage vec surface; straceShot(from, gs.sg[i], &surface); static vector<int> exclude; exclude.setsize(0); exclude.add(owner.clientnum); shot(owner, from, gs.sg[i], pos, weap, FRAG_NONE, surface, exclude, 0, 0, &hits); } loopv(clients) { // apply damage client &t = *clients[i]; clientstate &ts = t.state; // basic checks if (t.type == ST_EMPTY || ts.state != CS_ALIVE) continue; int damage = 0, shotgunflags = 0; float bestdist = 0; loopvrev(hits) if (hits[i].target == &t) { damage += hits[i].damage; shotgunflags |= hits[i].flags; // merge crit, etc. if (hits[i].dist > bestdist) bestdist = hits[i].dist; hits.remove(i/*--*/); } if (!damage) continue; damagedealt += damage; shotgunflags |= damage >= SGGIB * HEALTHSCALE ? FRAG_GIB : FRAG_NONE; if (m_progressive(gamemode, mutators) && shotgunflags & FRAG_GIB) damage = max(damage, 350 * HEALTHSCALE); serverdamage(t, owner, damage, weap, shotgunflags, from, bestdist); } return damagedealt; } </source> [[Category:Source_code]]
Summary:
Please note that all contributions to the AssaultCube Reloaded Wiki are considered to be released under the CC-BY-SA
Cancel
Editing help
(opens in new window)
Follow on IG
TikTok
Join Fan Lab