/** * Calculates the trajectory for a curved path. * @param accuracy The unit's accuracy. * @param doTestTrajectory * @return True when a trajectory is possible. */ int Projectile::calculateThrow(double accuracy, bool doTestTrajectory) { Tile *targetTile = _save->getTile(_action.target); Position originVoxel = _save->getTileEngine()->getOriginVoxel(_action, 0); Position targetVoxel = _action.target * Position(16,16,24) + Position(8,8, (2 + -targetTile->getTerrainLevel())); if (_action.type != BA_THROW) { BattleUnit *tu = targetTile->getUnit(); if (!tu && _action.target.z > 0 && targetTile->hasNoFloor(0)) tu = _save->getTile(_action.target - Position(0, 0, 1))->getUnit(); if (tu) { targetVoxel.z += ((tu->getHeight()/2) + tu->getFloatHeight()) - 2; } } double curvature = 0.0; int retVal = V_OUTOFBOUNDS; if (_save->getTileEngine()->validateThrow(_action, originVoxel, targetVoxel, &curvature, &retVal)) { if (doTestTrajectory) { return V_FLOOR; // retVal; } int test = V_OUTOFBOUNDS; // finally do a line calculation and store this trajectory, make sure it's valid. while (test == V_OUTOFBOUNDS) { Position deltas = targetVoxel; // apply some accuracy modifiers applyAccuracy(originVoxel, &deltas, accuracy, true, _save->getTile(_action.target), false); //calling for best flavor deltas -= targetVoxel; _trajectory.clear(); test = _save->getTileEngine()->calculateParabola(originVoxel, targetVoxel, true, &_trajectory, _action.actor, curvature, deltas); Position endPoint = _trajectory.back(); endPoint.x /= 16; endPoint.y /= 16; endPoint.z /= 24; Tile *endTile = _save->getTile(endPoint); // check if the item would land on a tile with a blocking object if (_action.type == BA_THROW && endTile && endTile->getMapData(MapData::O_OBJECT) && endTile->getMapData(MapData::O_OBJECT)->getTUCost(MT_WALK) == 255) { test = V_OUTOFBOUNDS; } } return retVal; } return V_OUTOFBOUNDS; }
int Projectile::calculateTrajectory(double accuracy, Position originVoxel) { Tile *targetTile = _save->getTile(_action.target); BattleUnit *bu = _action.actor; int test = _save->getTileEngine()->calculateLine(originVoxel, _targetVoxel, false, &_trajectory, bu); if (test != V_EMPTY && !_trajectory.empty() && _action.actor->getFaction() == FACTION_PLAYER && _action.autoShotCounter == 1 && ((SDL_GetModState() & KMOD_CTRL) == 0 || !Options::forceFire) && _save->getBattleGame()->getPanicHandled() && _action.type != BA_LAUNCH) { Position hitPos = Position(_trajectory.at(0).x/16, _trajectory.at(0).y/16, _trajectory.at(0).z/24); if (test == V_UNIT && _save->getTile(hitPos) && _save->getTile(hitPos)->getUnit() == 0) //no unit? must be lower { hitPos = Position(hitPos.x, hitPos.y, hitPos.z-1); } if (hitPos != _action.target && _action.result.empty()) { if (test == V_NORTHWALL) { if (hitPos.y - 1 != _action.target.y) { _trajectory.clear(); return V_EMPTY; } } else if (test == V_WESTWALL) { if (hitPos.x - 1 != _action.target.x) { _trajectory.clear(); return V_EMPTY; } } else if (test == V_UNIT) { BattleUnit *hitUnit = _save->getTile(hitPos)->getUnit(); BattleUnit *targetUnit = targetTile->getUnit(); if (hitUnit != targetUnit) { _trajectory.clear(); return V_EMPTY; } } else { _trajectory.clear(); return V_EMPTY; } } } _trajectory.clear(); bool extendLine = true; // even guided missiles drift, but how much is based on // the shooter's faction, rather than accuracy. if (_action.type == BA_LAUNCH) { if (_action.actor->getFaction() == FACTION_PLAYER) { accuracy = 0.60; } else { accuracy = 0.55; } extendLine = _action.waypoints.size() <= 1; } // apply some accuracy modifiers. // This will results in a new target voxel applyAccuracy(originVoxel, &_targetVoxel, accuracy, false, targetTile, extendLine); // finally do a line calculation and store this trajectory. return _save->getTileEngine()->calculateLine(originVoxel, _targetVoxel, true, &_trajectory, bu); }
/** * calculateTrajectory. * @return true when a trajectory is possible. */ bool Projectile::calculateTrajectory(double accuracy) { Position originVoxel, targetVoxel; int direction; int dirYshift[8] = {1, 1, 8, 15, 15, 15, 8, 1 }; int dirXshift[8] = {8, 14, 15, 15, 8, 1, 1, 1 }; // large units : x2 originVoxel = Position(_origin.x*16, _origin.y*16, _origin.z*24); originVoxel.z += -_save->getTile(_origin)->getTerrainLevel(); BattleUnit *bu = _save->getTile(_origin)->getUnit(); originVoxel.z += bu->isKneeled()?bu->getUnit()->getKneelHeight():bu->getUnit()->getStandHeight(); originVoxel.z -= 3; if (originVoxel.z >= (_origin.z + 1)*24) { _origin.z++; } direction = bu->getDirection(); originVoxel.x += dirXshift[direction]; originVoxel.y += 15-dirYshift[direction]; // determine the target voxel. // aim at the center of the unit, the object, the walls or the floor (in that priority) // if there is no LOF to the center, try elsewhere (more outward). // Store this target voxel. Tile *tile = _save->getTile(_target); if (tile->getUnit() != 0) { if (_origin == _target) { targetVoxel = Position(_target.x*16 + 8, _target.y*16 + 8, _target.z*24); } else { targetVoxel = Position(_target.x*16 + 8, _target.y*16 + 8, _target.z*24 + tile->getUnit()->getUnit()->getStandHeight()/2); } } else if (tile->getMapData(O_OBJECT) != 0) { targetVoxel = Position(_target.x*16 + 8, _target.y*16 + 8, _target.z*24 + 10); } else if (tile->getMapData(O_NORTHWALL) != 0) { targetVoxel = Position(_target.x*16 + 8, _target.y*16 + 16, _target.z*24 + 10); } else if (tile->getMapData(O_WESTWALL) != 0) { targetVoxel = Position(_target.x*16, _target.y*16 + 8, _target.z*24 + 10); } else if (tile->getMapData(O_FLOOR) != 0) { targetVoxel = Position(_target.x*16 + 8, _target.y*16 + 8, _target.z*24); } else { return false; // no line of fire } // apply some accuracy modifiers (todo: calculate this) // This will results in a new target voxel applyAccuracy(originVoxel, &targetVoxel, accuracy); // finally do a line calculation and store this trajectory. _save->getTerrainModifier()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); return true; }
/** * calculateTrajectory. * @return the objectnumber(0-3) or unit(4) or out of map (5) or -1(no line of fire) */ int Projectile::calculateTrajectory(double accuracy) { Position originVoxel, targetVoxel; Tile *targetTile = 0; int direction; int dirYshift[24] = {1, 3, 9, 15, 15, 13, 7, 1, 1, 1, 7, 13, 15, 15, 9, 3, 1, 2, 8, 14, 15, 14, 8, 2}; int dirXshift[24] = {9, 15, 15, 13, 8, 1, 1, 3, 7, 13, 15, 15, 9, 3, 1, 1, 8, 14, 15, 14, 8, 2, 1, 2}; int offset = 0; originVoxel = Position(_origin.x*16, _origin.y*16, _origin.z*24); BattleUnit *bu = _action.actor; if (bu->getArmor()->getSize() > 1) { offset = 16; } else if(_action.weapon == _action.weapon->getOwner()->getItem("STR_LEFT_HAND") && !_action.weapon->getRules()->isTwoHanded()) { offset = 8; } // take into account soldier height and terrain level if the projectile is launched from a soldier if (_action.actor->getPosition() == _origin) { // calculate offset of the starting point of the projectile originVoxel.z += -_save->getTile(_origin)->getTerrainLevel(); originVoxel.z += bu->getHeight() + bu->getFloatHeight(); originVoxel.z -= 4; if (originVoxel.z >= (_origin.z + 1)*24) { _origin.z++; } direction = bu->getDirection(); if (bu->getTurretType() != -1) direction = bu->getTurretDirection(); originVoxel.x += dirXshift[direction+offset]*bu->getArmor()->getSize(); originVoxel.y += dirYshift[direction+offset]*bu->getArmor()->getSize(); } else { // don't take into account soldier height and terrain level if the projectile is not launched from a soldier(from a waypoint) originVoxel.x += 8; originVoxel.y += 8; originVoxel.z += 12; } if (_action.type == BA_LAUNCH || (SDL_GetModState() & KMOD_CTRL) != 0) { // target nothing, targets the middle of the tile targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 12); } else { // determine the target voxel. // aim at the center of the unit, the object, the walls or the floor (in that priority) // if there is no LOF to the center, try elsewhere (more outward). // Store this target voxel. targetTile = _save->getTile(_action.target); Position hitPos; int test = -1; if (targetTile->getUnit() != 0) { if (_origin == _action.target || targetTile->getUnit() == _action.actor) { // don't shoot at yourself but shoot at the floor targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } else { _save->getTileEngine()->canTargetUnit(&originVoxel, targetTile, &targetVoxel, bu); } } else if (targetTile->getMapData(MapData::O_OBJECT) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_OBJECT, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 10); } } else if (targetTile->getMapData(MapData::O_NORTHWALL) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_NORTHWALL, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16, _action.target.z*24 + 9); } } else if (targetTile->getMapData(MapData::O_WESTWALL) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_WESTWALL, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16, _action.target.y*16 + 8, _action.target.z*24 + 9); } } else if (targetTile->getMapData(MapData::O_FLOOR) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_FLOOR, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } } else { // target nothing, targets the middle of the tile targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 10); } test = _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, false, &_trajectory, bu); if (test == 4 && !_trajectory.empty()) { hitPos = Position(_trajectory.at(0).x/16, _trajectory.at(0).y/16, _trajectory.at(0).z/24); if (_save->getTile(hitPos) && _save->getTile(hitPos)->getUnit() == 0) //no unit? must be lower { hitPos = Position(hitPos.x, hitPos.y, hitPos.z-1); } } if (test != -1 && !_trajectory.empty() && _action.actor->getFaction() == FACTION_PLAYER && _action.autoShotCounter == 1) { //skip already estimated hitPos if (test != 4) { hitPos = Position(_trajectory.at(0).x/16, _trajectory.at(0).y/16, _trajectory.at(0).z/24); } if (hitPos != _action.target && _action.result == "") { if (test == 2) { if (hitPos.y - 1 == _action.target.y) { _trajectory.clear(); return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); } } if (test == 1) { if (hitPos.x - 1 == _action.target.x) { _trajectory.clear(); return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); } } _trajectory.clear(); return -1; } } _trajectory.clear(); } // apply some accuracy modifiers (todo: calculate this) // This will results in a new target voxel if (_action.type != BA_LAUNCH) applyAccuracy(originVoxel, &targetVoxel, accuracy, false, targetTile); // finally do a line calculation and store this trajectory. return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); }
/** * calculateTrajectory. * @return the objectnumber(0-3) or unit(4) or out of map (5) or -1(no line of fire) */ int Projectile::calculateTrajectory(double accuracy) { Position originVoxel, targetVoxel; int direction; int dirYshift[8] = {1, 1, 8, 15, 15, 15, 8, 1 }; int dirXshift[8] = {8, 14, 15, 15, 8, 1, 1, 1 }; // large units : x2 originVoxel = Position(_origin.x*16, _origin.y*16, _origin.z*24); originVoxel.z += -_save->getTile(_origin)->getTerrainLevel(); BattleUnit *bu = _save->getTile(_origin)->getUnit(); originVoxel.z += bu->getHeight(); originVoxel.z -= 3; if (originVoxel.z >= (_origin.z + 1)*24) { _origin.z++; } direction = bu->getDirection(); originVoxel.x += dirXshift[direction]; originVoxel.y += dirYshift[direction]; // determine the target voxel. // aim at the center of the unit, the object, the walls or the floor (in that priority) // if there is no LOF to the center, try elsewhere (more outward). // Store this target voxel. Tile *tile = _save->getTile(_action.target); if (tile->getUnit() != 0) { if (_origin == _action.target) { // don't shoot at yourself but shoot at the floor targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } else { // first try is at half the unit height targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + tile->getUnit()->getUnit()->getStandHeight()/2); int test = _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, false, &_trajectory, bu); _trajectory.clear(); if (test != 4) { // did not hit a unit, try at different heights (for ex: unit behind a window can only be hit in the head) targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + (tile->getUnit()->getUnit()->getStandHeight()*3)/4); test = _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, false, &_trajectory, bu); _trajectory.clear(); } } } else if (tile->getMapData(MapData::O_OBJECT) != 0) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 10); } else if (tile->getMapData(MapData::O_NORTHWALL) != 0) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16, _action.target.z*24 + 10); } else if (tile->getMapData(MapData::O_WESTWALL) != 0) { targetVoxel = Position(_action.target.x*16, _action.target.y*16 + 8, _action.target.z*24 + 10); } else if (tile->getMapData(MapData::O_FLOOR) != 0) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } else { return -1; // no line of fire } // apply some accuracy modifiers (todo: calculate this) // This will results in a new target voxel applyAccuracy(originVoxel, &targetVoxel, accuracy); // finally do a line calculation and store this trajectory. return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); }