def TryPickSphereBorder(self): matches = [] scannerWnd = Scanner.GetIfOpen() if scannerWnd: x, y = uicore.ScaleDpi(uicore.uilib.x), uicore.ScaleDpi(uicore.uilib.y) ray, start = GetRayAndPointFromScreen(x, y) pickRadiusRay, pickRadiusStart = GetRayAndPointFromScreen(x - 30, y) camera = sm.GetService('sceneManager').GetRegisteredCamera('systemmap') if camera is None: return viewDir = geo2.QuaternionTransformVector(camera.rotationAroundParent, (0.0, 0.0, 1.0)) viewDir = geo2.Vec3Normalize(viewDir) targetPlaneNormal = geo2.Vector(*viewDir) scanSvc = sm.StartService('scanSvc') probeData = scanSvc.GetProbeData() probes = scannerWnd.GetProbeSpheres() for probeID, probeControl in probes.iteritems(): if probeID not in probeData or probeData[probeID].state != const.probeStateIdle: continue targetPlanePos = geo2.Vector(probeControl.locator.worldTransform[3][0], probeControl.locator.worldTransform[3][1], probeControl.locator.worldTransform[3][2]) rad = list(probeControl.sphere.scaling)[0] * SYSTEMMAP_SCALE pos = RayToPlaneIntersection(start, ray, targetPlanePos, targetPlaneNormal) picRadiusPos = RayToPlaneIntersection(pickRadiusStart, pickRadiusRay, targetPlanePos, targetPlaneNormal) pickRad = (trinity.TriVector(*picRadiusPos) - trinity.TriVector(*pos)).Length() diffFromPickToSphereBorder = (trinity.TriVector(*targetPlanePos) - trinity.TriVector(*pos)).Length() if rad + pickRad > diffFromPickToSphereBorder > rad - pickRad: matches.append((abs(rad - diffFromPickToSphereBorder), probeControl)) if matches: matches = uiutil.SortListOfTuples(matches) return matches[0]
def __init__(self, worldSpaceTypeID, worldSpaceID): self.worldSpaceTypeID = worldSpaceTypeID self.worldSpaceID = worldSpaceID if not hasattr(self, 'mainRow'): self.mainRow = cfg.worldspaces.Get(worldSpaceTypeID) self.boundingBox = (geo2.Vector(0, 0, 0), geo2.Vector(0, 0, 0)) self.ground = None
def ScaleProbesAroundCenter(self): x, y = uicore.ScaleDpi(uicore.uilib.x), uicore.ScaleDpi(uicore.uilib.y) mousePos = geo2.Vector(x, y, 0) probeData = sm.GetService('scanSvc').GetProbeData() scannerWnd = form.Scanner.GetIfOpen() if scannerWnd is None: return probes = scannerWnd.GetProbeSpheres() centroid = geo2.Vector(0, 0, 0) numProbes = 0 for probeID, probeControl in probes.iteritems(): if probeID not in probeData or probeData[ probeID].state != const.probeStateIdle: continue probePos = probeControl.GetWorldPosition() centroid += probePos numProbes += 1 if numProbes <= 1: return centroid /= numProbes projectionParams = GetWorldToScreenParameters() centroidTansform = ((SYSTEMMAP_SCALE, 0, 0, 0), (0, SYSTEMMAP_SCALE, 0, 0), (0, 0, SYSTEMMAP_SCALE, 0), (centroid.x, centroid.y, centroid.z, 1.0)) screenCentroid = geo2.Vector( *ProjectTransform(projectionParams, centroidTansform)) screenCentroid.z = 0 probeScreenPos = geo2.Vector(*ProjectTransform( projectionParams, self.sr.movingProbe.locator.worldTransform)) probeScreenPos.z = 0 centerToProbe = probeScreenPos - screenCentroid centerToProbeLength = geo2.Vec2Length(centerToProbe) if centerToProbeLength < 0.1: return centerToProbeNormal = centerToProbe / centerToProbeLength toMouseDotProduct = geo2.Vec2Dot(mousePos - screenCentroid, centerToProbeNormal) projectedPos = screenCentroid + toMouseDotProduct * centerToProbeNormal toProjectedLength = geo2.Vec2Length(projectedPos - screenCentroid) if toProjectedLength < 0.1: return moveScale = toProjectedLength / centerToProbeLength if toMouseDotProduct < 0: moveScale = -moveScale for probeID, probeControl in probes.iteritems(): if probeID not in probeData or probeData[ probeID].state != const.probeStateIdle: continue pos = probeControl.GetWorldPosition() toProbe = pos - centroid endPos = centroid + toProbe * moveScale endPos = (endPos.x / SYSTEMMAP_SCALE, endPos.y / SYSTEMMAP_SCALE, endPos.z / SYSTEMMAP_SCALE) probeControl.SetPosition(endPos) scannerWnd.ShowCentroidLines() scannerWnd.HighlightProbeIntersections() sm.GetService('systemmap').HighlightItemsWithinProbeRange()
def GetGeometry(mlist): lines = [] for i in range(0, len(mlist), 2): lines.append(geo2.Vector(*mlist[i])) lines.append(geo2.Vector(*mlist[i + 1])) return lines
def DrawLineSetCircle(lineSet, centerPosition, outerPosition, segmentSize, lineColor = (0.3, 0.3, 0.3, 0.5), lineWeight = 2.0, animationSpeed = 0.0, dashSegments = 0, dashColor = None): orbitPos = geo2.Vector(*outerPosition) parentPos = geo2.Vector(*centerPosition) dirVec = orbitPos - parentPos radius = geo2.Vec3Length(dirVec) fwdVec = (1.0, 0.0, 0.0) dirVec = geo2.Vec3Normalize(dirVec) rotation = geo2.QuaternionRotationArc(fwdVec, dirVec) matrix = geo2.MatrixAffineTransformation(1.0, (0.0, 0.0, 0.0), rotation, centerPosition) circum = math.pi * 2 * radius steps = min(256, max(16, int(circum / segmentSize))) coordinates = [] stepSize = math.pi * 2 / steps for step in range(steps): angle = step * stepSize x = math.cos(angle) * radius z = math.sin(angle) * radius pos = geo2.Vector(x, 0.0, z) pos = geo2.Vec3TransformCoord(pos, matrix) coordinates.append(pos) lineIDs = set() dashColor = dashColor or lineColor for start in xrange(steps): end = (start + 1) % steps lineID = lineSet.AddStraightLine(coordinates[start], lineColor, coordinates[end], lineColor, lineWeight) lineIDs.add(lineID) if dashSegments: lineSet.ChangeLineAnimation(lineID, dashColor, animationSpeed, dashSegments) return lineIDs
def Orbit(self, yaw, pitch): dev = trinity.device self.Focus(self.pointOfInterest) up = geo2.Vector(0.0, 1.0, 0.0) t = geo2.Vector(self.localViewMatrix[1][0], self.localViewMatrix[1][1], self.localViewMatrix[1][2]) if geo2.Vec3Dot(t, up) <= 0.0: pitch = -pitch yaw = -yaw pos = self.GetPosition() target = self.pointOfInterest view = geo2.Subtract(pos, target) view = geo2.Vec3Normalize(view) right = geo2.Vec3Cross(view, up) mat = self.localViewMatrix ipmat = geo2.MatrixTranslation(-target[0], -target[1], -target[2]) pmat = geo2.MatrixTranslation(target[0], target[1], target[2]) mat = geo2.MatrixInverse(mat) yrotMat = geo2.MatrixRotationAxis(up, yaw) rrotMat = geo2.MatrixRotationAxis(right, pitch) yrotMat = geo2.MatrixMultiply(yrotMat, rrotMat) mat = geo2.MatrixMultiply(mat, ipmat) mat = geo2.MatrixMultiply(mat, yrotMat) mat = geo2.MatrixMultiply(mat, pmat) self._position = geo2.MatrixDecompose(mat)[2] mat = geo2.MatrixInverse(mat) self.localViewMatrix = mat
def PrepareComponent(self, sceneID, entityID, component): """ Gets called in order to prepare a component. No other components can be referred to """ if sceneID not in self.sceneManagers: self.LogError('SceneID in prepare perception component has no previous manager ', sceneID, entityID) return component.entityID = entityID component.entityTypeID = const.perception.PERCEPTION_ENTITY_TYPE_TO_ID.get(component.entityTypeString) if component.entityTypeID == -1: self.LogError('entity in prepare perception component has missing type', entityID) return if component.behaviorSensesID == -1 or component.behaviorFiltersID == -1 or component.behaviorDecaysID == -1: self.LogError('entity in prepare perception component has missing state behaviorIDs', entityID) return component.sensorOffset = geo2.Vector(0.0, 0.0, 0.0) if component.sensorOffsetString != '': component.sensorOffsetString = component.sensorOffsetString.strip('()') sensorOffsetTuple = component.sensorOffsetString.split(',') try: component.sensorOffset = geo2.Vector(float(sensorOffsetTuple[0]), float(sensorOffsetTuple[1]), float(sensorOffsetTuple[2])) except: self.LogError('entity has invalid perception sensor offset value', entityID) component.sensorOffset = geo2.Vector(0.0, 0.0, 0.0) self.entityService.entitySceneManager.PrepareComponent(entityID, sceneID, component)
def testPoints(self): vMin = geo2.Vector(0, 0, 0) vMax = geo2.Vector(1, 1, 1) vStep = geo2.Vector(1, 1, 1) bench = WorldSpaceBenchmark(None, 'Test') pointList = bench.GeneratePositions(vMin, vMax, vStep) self.assertTrue(len(pointList) == 48)
def PickObject(self, x, y): if self.sceneManager.GetActiveScene() != self.renderScene: return rescale = 1.0 / 10000.0 projection = trinity.TriProjection() projection.PerspectiveFov(trinity.GetFieldOfView(), trinity.GetAspectRatio(), trinity.GetFrontClip(), trinity.GetBackClip()) view = trinity.TriView() view.transform = trinity.GetViewTransform() scaling, rotation, translation = geo2.MatrixDecompose( self.transform.worldTransform) direction = geo2.Vector(*translation) - geo2.Vector( view.transform[0][0], view.transform[1][0], view.transform[2][0]) if geo2.Vec3Dot( geo2.Vec3Normalize(direction), geo2.Vector(-view.transform[0][2], -view.transform[1][2], -view.transform[2][2])) < 0: return self.renderObject.translation = geo2.Vec3Scale(translation, rescale) self.renderObject.rotation = rotation self.renderObject.scaling = geo2.Vec3Scale(scaling, rescale) scaling, rotation, translation = geo2.MatrixDecompose(view.transform) translation = geo2.Vec3Scale(translation, rescale) view.transform = geo2.MatrixTransformation(None, None, scaling, None, rotation, translation) return self.renderObject.PickObject(x, y, projection, view, trinity.device.viewport)
def IsResultWithinWarpDistance(result): ballpark = sm.GetService('michelle').GetBallpark() egoBall = ballpark.GetBall(ballpark.ego) egoPos = geo2.Vector(egoBall.x, egoBall.y, egoBall.z) resultPos = geo2.Vector(*result.data) distanceSquared = geo2.Vec3LengthSq(egoPos - resultPos) return distanceSquared > MIN_WARP_DISTANCE_SQUARED
def _GetViewMatrixFromAngle(self, cameraAngle, lookAt, cameraDistance): view_at = geo2.Vector(lookAt[0], lookAt[1], lookAt[2]) view_eye = geo2.Vector(0.0, 0.0, 1.0) angleTransform = geo2.MatrixRotationYawPitchRoll(cameraAngle[0], cameraAngle[1], cameraAngle[2]) view_eye = geo2.Vec3TransformCoord(view_eye, angleTransform) view_eye = geo2.Vector(*view_eye) view_eye = view_eye * cameraDistance + view_at return (view_eye, view_at, geo2.Vector(*GETPHOTO_VIEW_UP))
def ProjectPointTowardsFrontPlane(point, dist=7.0): ppos = point viewMat = trinity.GetViewTransform() cpos = geo2.Vector(*trinity.GetViewPosition()) dir = geo2.Vec3Normalize(geo2.Subtract(cpos, ppos)) lookat = geo2.Vector(viewMat[0][2], viewMat[1][2], viewMat[2][2]) point = cpos + lookat * dist return RayToPlaneIntersection(ppos, dir, point, lookat)
def ShiftPosition(self, translation): """ translate the coordinates translation: x,y,z coordinates as tuple or geo2.Vector """ newPos = geo2.Vector(*self.locator.translation) + geo2.Vector( *translation) self.SetPosition(newPos)
def GetRayAndPointFromScreen(x, y): proj, view, vp = uix.GetFullscreenProjectionViewAndViewport() ray, start = trinity.device.GetPickRayFromViewport(x, y, vp, view.transform, proj.transform) ray = geo2.Vector(*ray) start = geo2.Vector(*start) return (ray, start)
def _PickPointToPath(self): picked = self.cameraClient.GetActiveCamera().PerformPick(uicore.uilib.x, uicore.uilib.y, session.charid) if picked is None: return else: point = geo2.Vector(*picked[0]) height = self.gameWorldClient.GetFloorHeight(point, session.worldspaceid) destination = geo2.Vector(point.x, height, point.z) return destination
def GetRayAndPointFromScreen(self): x = float(uicore.uilib.x) y = float(uicore.uilib.y) data = self.GetCameraMatrixes() start = geo2.Vec3Unproject((x, y, 0.0), *data) end = geo2.Vec3Unproject((x, y, 100000.0), *data) ray = geo2.Vec3SubtractD(end, start) ray = geo2.Vector(*ray) start = geo2.Vector(*start) return (ray, start)
def EntityLOS(self, startEntity, endPosition, offset=1.8): vStart = geo2.Vector(*startEntity.GetComponent('position').position) vEnd = geo2.Vector(endPosition[0], endPosition[1], endPosition[2]) gameWorld = self.GetGameWorld(startEntity.scene.sceneID) if not gameWorld: return False vStart.y += offset vEnd.y += offset tRes = gameWorld.LineTest(vStart, vEnd) return tRes == None
def Pan(self, dx = 0, dy = 0): if self.panTarget is None: self.panTarget = geo2.Vector(0, 0) if self.IsClampedHorizontally(): dx = 0 if self.IsClampedVertically(): dy = 0 self.panTarget += geo2.Vector(dx, dy) if not self.panUpdateThread: self.panUpdateThread = uthread.new(self._PanUpdateThread)
def GetSynchedAnimPosition(entity): model = sm.GetService('animationClient').GetEntityModel(entity.entityID) if model is not None: pos = geo2.Vector(model.translation[0], model.translation[1], model.translation[2]) if ShouldSnapEntityPositionToGround(entity): pos = geo2.Vector(*GetEntityTrajectoryPosition(model, entity.GetComponent('animation').controller.animationNetwork)) height = sm.GetService('gameWorldClient').GetFloorHeight(pos, entity.scene.sceneID) pos.y = height return pos return entity.GetComponent('position').position
def LoadSolarSystemMap(self): self.maxRadius = 0.0 solarsystemID = self.solarsystemID parent = self.systemMapTransform solarSystemData = self.systemMapSvc.GetSolarsystemData(solarsystemID) planets = [] childrenToParentByID = {} sunID = None maxRadius = 0.0 for celestialObject in solarSystemData: if celestialObject.groupID == const.groupPlanet: planets.append( (celestialObject.itemID, geo2.Vector(celestialObject.x, celestialObject.y, celestialObject.z))) elif celestialObject.groupID == const.groupSun: sunID = celestialObject.itemID for each in solarSystemData: if each.groupID in (const.groupPlanet, const.groupStargate): childrenToParentByID[each.itemID] = sunID continue closest = [] eachPosition = geo2.Vector(each.x, each.y, each.z) for planetID, planetPos in planets: diffPos = planetPos - eachPosition diffVector = geo2.Vec3Length(diffPos) closest.append((diffVector, planetID)) maxRadius = max(maxRadius, diffVector) closest.sort() childrenToParentByID[each.itemID] = planets[0][1] self.maxRadius = maxRadius orbits = [] objectTransforms = {} pm = (const.groupPlanet, const.groupMoon) for each in solarSystemData: if each.itemID == each.locationID: continue if each.groupID == const.groupSecondarySun: continue if each.groupID in pm: parentID = childrenToParentByID.get(each.itemID, None) if parentID: orbits.append([each.itemID, parentID]) transform = trinity.EveTransform() transform.translation = (each.x, each.y, each.z) transform.name = str(each.itemID) parent.children.append(transform) objectTransforms[each.itemID] = transform uthread.new(self.CreateOrbits, orbits, objectTransforms) self.solarSystemRadius = maxRadius cfg.evelocations.Prime(objectTransforms.keys(), 0)
def LoadSolarSystemMap(self): self.maxRadius = 0.0 solarsystemID = self.solarsystemID parent = self.systemMapTransform solarSystemData = self.systemMapSvc.GetSolarsystemData(solarsystemID) planets = [] childrenToParentByID = {} sunID = None maxRadius = 0.0 for celestialObject in solarSystemData: if celestialObject.groupID == const.groupPlanet: planets.append((celestialObject.itemID, geo2.Vector(celestialObject.x, celestialObject.y, celestialObject.z))) maxRadius = max(maxRadius, geo2.Vec3Length((celestialObject.x, celestialObject.y, celestialObject.z))) elif celestialObject.groupID == const.groupSun: sunID = celestialObject.itemID sunGraphicFilePath = GetGraphicFile(evetypes.GetGraphicID(celestialObject.typeID)) sunGraphicFile = trinity.Load(sunGraphicFilePath) self.CreateSun(sunGraphicFile) self.sunID = sunID objectPositions = {} for each in solarSystemData: objectPositions[each.itemID] = (each.x, each.y, each.z) if each.groupID in (const.groupPlanet, const.groupStargate): childrenToParentByID[each.itemID] = sunID continue closest = [] eachPosition = geo2.Vector(each.x, each.y, each.z) maxRadius = max(maxRadius, geo2.Vec3Length(eachPosition)) for planetID, planetPos in planets: diffPos = planetPos - eachPosition diffVector = geo2.Vec3Length(diffPos) closest.append((diffVector, planetID)) closest.sort() childrenToParentByID[each.itemID] = closest[0][1] self.maxRadius = maxRadius for each in solarSystemData: if each.itemID == each.locationID: continue if each.groupID == const.groupSecondarySun: continue if each.groupID == const.groupPlanet: self.CreatePlanet((each.x, each.y, each.z)) OrbitCircle(each.itemID, (each.x, each.y, each.z), objectPositions[sunID], self.systemMapTransform) elif each.groupID == const.groupMoon: parentID = childrenToParentByID.get(each.itemID, None) if parentID: self.CreatePlanet((each.x, each.y, each.z)) OrbitCircle(each.itemID, (each.x, each.y, each.z), objectPositions[parentID], self.systemMapTransform) self.solarSystemRadius = maxRadius cfg.evelocations.Prime(objectPositions.keys(), 0)
def Pan(self, dx=0, dy=0): """ Pan along current camera xyz coordinates """ if self.panTarget is None: self.panTarget = geo2.Vector(0, 0) if self.IsClampedHorizontally(): dx = 0 if self.IsClampedVertically(): dy = 0 self.panTarget += geo2.Vector(dx, dy) if not self.panUpdateThread: self.panUpdateThread = uthread.new(self._PanUpdateThread)
def EntityLOS(self, startEntity, endPosition, offset = 1.8): """ Returns True if the entity is within line of sight of the location. """ vStart = geo2.Vector(*startEntity.GetComponent('position').position) vEnd = geo2.Vector(endPosition[0], endPosition[1], endPosition[2]) gameWorld = self.GetGameWorld(startEntity.scene.sceneID) if not gameWorld: return False vStart.y += offset vEnd.y += offset tRes = gameWorld.LineTest(vStart, vEnd) return tRes == None
def Prepare(self): StretchEffect.Prepare(self) self.gfxModel.source.behavior = trinity.EveLocalPositionBehavior.damageLocator self.gfxModel.source.parent = self.GetEffectShipBall().model self.gfxModel.dest.behavior = trinity.EveLocalPositionBehavior.damageLocator self.gfxModel.dest.parent = self.GetEffectTargetBall().model p = self.GetEffectShipBall().GetVectorAt(blue.os.GetSimTime()) sourcePos = geo2.Vector(p.x, p.y, p.z) p = self.GetEffectTargetBall().GetVectorAt(blue.os.GetSimTime()) targetPos = geo2.Vector(p.x, p.y, p.z) sourceToTargetDir = geo2.Vec3Direction(sourcePos, targetPos) uthread.new(self.CreateImpact, sourceToTargetDir)
def _PickPointToPath(self): """ Picks the point currently under the cursor and does some fancywork to try and find the floor. Returns a tuple representing the position to path too. """ picked = self.cameraClient.GetActiveCamera().PerformPick(uicore.uilib.x, uicore.uilib.y, session.charid) if picked is None: return else: point = geo2.Vector(*picked[0]) height = self.gameWorldClient.GetFloorHeight(point, session.worldspaceid) destination = geo2.Vector(point.x, height, point.z) return destination
def SetupInteriourCamera(sceneContainer, boundingBox): p0, p1 = geo2.Vector(boundingBox[0]), geo2.Vector(boundingBox[1]) center = 0.5 * (p1 - p0) + p0 sceneContainer.cameraParent.parent = None sceneContainer.cameraParent.value = center sceneContainer.verticalPanLimits = (p0.y, p1.y) rad = max(geo2.Vec3Length(p0 - p1), 0.3) alpha = sceneContainer.fieldOfView * 1.5 / 2.0 maxZoom = min(rad * (1 / math.tan(alpha)), 9.0) minZoom = rad + sceneContainer.frontClip sceneContainer.SetMinMaxZoom(minZoom, maxZoom) sceneContainer.zoom = 0.6 sceneContainer.camera.maxPitch = 0.0
def Render(self): if self.display: viewTrans = trinity.GetViewTransform() view = geo2.Vector(viewTrans[0][2], viewTrans[1][2], viewTrans[2][2]) localpos = self.GetTranslation() campos = geo2.Vector(*trinity.GetViewPosition()) vec = localpos - campos vec = geo2.Vec3Normalize(vec) if geo2.Vec3Dot(vec, view) > 0.0: mat = self.GetTargetPlaneProjectionMatrix() areas = self.geometry.Update(mat) self.geometry.Render(self.Cull(areas))
def GetSelectionRotation(self): if self.currentCursor != 'Rotation' or not len(self.selection): return geo2.Vector(0, 0, 0, 1) if self.currentHardGroup in self.hardGroupRotations: return self.GetHardGroupRotation(self.currentHardGroup) if len(self.selection) > 1: return self.groupRotation ballID = self.selection[0] if ballID in self.fakeTransforms: self.fakeTransforms[ballID].Update(blue.os.GetSimTime()) targetBall = self.fakeTransforms[ballID].rotationCurve return geo2.Vector(targetBall.value.x, targetBall.value.y, targetBall.value.z, targetBall.value.w)
def Update(self): if self.updateFocus: right = self.GetBonePosition('fj_eyeballRight') left = self.GetBonePosition('fj_eyeballLeft') self.focus = geo2.Add(right, left) self.focus = geo2.Vector(*self.focus) * 0.5 if self.moveCallback: self.moveCallback(self.viewMatrix) if self.controlStyle == CONTROL_VERTICAL: length = geo2.Vec2Length(geo2.Vector(self.focus[0], self.focus[2])) self.focus = (0.0, self.focus[1], length) self.updateFocus = False cameras.PolarCamera.Update(self)
def testGetSynchedAnimStartLocation(self): sourceEnt = self._CreateMovementEntity(geo2.Vector(0.0, 0.0, 0.0), geo2.Vector(0.0, 0.0, 0.0, 1.0)) targetEnt = self._CreateMovementEntity(geo2.Vector(0.0, 0.0, 2.0), geo2.Vector(0.0, 0.0, 0.0, 1.0)) animInfo = {const.animation.METADATA_START_DISTANCE: 1.0} newSourcePos, newSourceRot, newTargetPos, newTargetRot = GetSynchedAnimStartLocation( sourceEnt.GetComponent('position').position, sourceEnt.GetComponent('position').rotation, targetEnt.GetComponent('position').position, targetEnt.GetComponent('position').rotation, animInfo) self.assertTrue( mathCommon.VectorCloseEnough(newSourcePos, geo2.Vector(0.0, 0.0, 1.0)), "newSourcePos doesn't match expected value") self.assertTrue( mathCommon.VectorCloseEnough(newSourceRot, geo2.Vector(0.0, 0.0, 0.0, 1.0)), "newSourceRot doesn't match expected value") self.assertTrue( mathCommon.VectorCloseEnough(newTargetPos, geo2.Vector(0.0, 0.0, 2.0)), "newTargetPos doesn't match expected value") self.assertTrue( mathCommon.VectorCloseEnough(newTargetRot, geo2.Vector(0.0, 1.0, 0.0, 0.0)), "newTargetRot doesn't match expected value")