public void FindCollisionActorsByRadius(float x, float y, float radius, Func <ActorBase, bool> callback) { AABB aabb = new AABB(x - radius, y - radius, x + radius, y + radius); collisions.Query((actor) => { if ((actor.CollisionFlags & CollisionFlags.CollideWithOtherActors) == 0) { return(true); } // Find the closest point to the circle within the rectangle float closestX = MathF.Clamp(x, actor.AABB.LowerBound.X, actor.AABB.UpperBound.X); float closestY = MathF.Clamp(y, actor.AABB.LowerBound.Y, actor.AABB.UpperBound.Y); // Calculate the distance between the circle's center and this closest point float distanceX = (x - closestX); float distanceY = (y - closestY); // If the distance is less than the circle's radius, an intersection occurs float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY); if (distanceSquared < (radius * radius)) { return(callback(actor)); } return(true); }, ref aabb); }
private void RenderTexturedBackground(IDrawDevice device) { if (!cachedTexturedBackground.IsAvailable) { return; } float timeMult = Time.TimeMult; backgroundX += timeMult * 1.2f; backgroundY += timeMult * -0.2f + timeMult * MathF.Sin(backgroundPhase) * 0.6f; backgroundPhase += timeMult * 0.001f; Vector3 renderPos = new Vector3(0, 0, 600); // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets renderPos.X = MathF.Round(renderPos.X); renderPos.Y = MathF.Round(renderPos.Y); if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2) { renderPos.X += 0.5f; } if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2) { // AMD Bugfix? renderPos.Y -= 0.004f; } // Reserve the required space for vertex data in our locally cached buffer int neededVertices = 4; if (cachedVertices == null || cachedVertices.Length < neededVertices) { cachedVertices = new VertexC1P3T2[neededVertices]; } // Render it as world-space fullscreen quad cachedVertices[0].Pos = new Vector3(renderPos.X, renderPos.Y, renderPos.Z); cachedVertices[1].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y, renderPos.Z); cachedVertices[2].Pos = new Vector3(renderPos.X + device.TargetSize.X, renderPos.Y + device.TargetSize.Y, renderPos.Z); cachedVertices[3].Pos = new Vector3(renderPos.X, renderPos.Y + device.TargetSize.Y, renderPos.Z); cachedVertices[0].TexCoord = new Vector2(0.0f, 0.0f); cachedVertices[1].TexCoord = new Vector2(1f, 0.0f); cachedVertices[2].TexCoord = new Vector2(1f, 1f); cachedVertices[3].TexCoord = new Vector2(0.0f, 1f); cachedVertices[0].Color = cachedVertices[1].Color = cachedVertices[2].Color = cachedVertices[3].Color = ColorRgba.White; // Setup custom pixel shader BatchInfo material = device.RentMaterial(); material.Technique = texturedBackgroundShader; material.MainTexture = cachedTexturedBackground; material.SetValue("horizonColor", horizonColor); material.SetValue("shift", new Vector2(backgroundX, backgroundY)); material.SetValue("parallaxStarsEnabled", 0f); device.AddVertices(material, VertexMode.Quads, cachedVertices, 0, 4); }
public override void Draw(IDrawDevice device) { if (gameobj == null) { return; } float timeMult = Time.TimeMult; for (int j = 0; j < circleEffectData.Length; j++) { ref CircleEffect circle = ref circleEffectData[j]; if (circle.Alpha <= 0f) { continue; } int segmentNum = MathF.Clamp(MathF.RoundToInt(MathF.Pow(circle.Radius, 0.65f) * 2.5f), 4, 32); float angle = 0.0f; for (int i = 0; i < segmentNum; i++) { vertices[i].Pos.X = circle.Pos.X + (float)Math.Sin(angle) * circle.Radius; vertices[i].Pos.Y = circle.Pos.Y - (float)Math.Cos(angle) * circle.Radius; vertices[i].Pos.Z = circle.Pos.Z - 10f; vertices[i].Color = new ColorRgba(1f, circle.Alpha); angle += (MathF.TwoPi / segmentNum); } device.AddVertices(material, VertexMode.LineLoop, vertices, 0, segmentNum); circle.Radius -= timeMult * 0.8f; circle.Alpha -= timeMult * 0.03f; }
public void AddGems(int count) { gems += count; attachedHud?.ShowGems(gems); PlaySound("PickupGem", 1f, MathF.Min(0.7f + gemsPitch * 0.05f, 1.3f)); gemsTimer = 120f; gemsPitch++; }
public void AddGems(int count) { gems += count; #if !SERVER attachedHud?.ShowGems(gems); #endif PlaySound("PickupGem", 1f, MathF.Min(0.7f + gemsPitch * 0.05f, 1.3f)); gemsTimer = 120f; gemsPitch++; #if MULTIPLAYER && SERVER ((LevelHandler)levelHandler).OnPlayerAddGems(this, count); #endif }
protected override async Task OnActivatedAsync(ActorActivationDetails details) { bridgeWidth = details.Params[0]; bridgeType = (BridgeType)details.Params[1]; if (bridgeType > BridgeType.Lab) { bridgeType = BridgeType.Rope; } int toughness = details.Params[2]; heightFactor = MathF.Sqrt((16 - toughness) * bridgeWidth) * 4f; // Request metadata here to allow async loading await RequestMetadataAsync("Bridge/" + bridgeType.ToString("G")); Vector3 pos = Transform.Pos; originalY = pos.Y - 6; bridgePieces = new List <Piece>(); int[] widthList = PieceWidths[(int)bridgeType]; int widthCovered = widthList[0] / 2; for (int i = 0; (widthCovered <= bridgeWidth * 16 + 6) || (i * 16 < bridgeWidth); i++) { Piece piece = new Piece(); piece.OnActivated(new ActorActivationDetails { Api = api, Pos = new Vector3(pos.X + widthCovered - 16, pos.Y - 20, LevelHandler.MainPlaneZ + 10), Params = new[] { (ushort)bridgeType, (ushort)i } }); api.AddActor(piece); bridgePieces.Add(piece); widthCovered += (widthList[i % widthList.Length] + widthList[(i + 1) % widthList.Length]) / 2; } collisionFlags = CollisionFlags.CollideWithOtherActors | CollisionFlags.SkipPerPixelCollisions; }
private void OnRender(IDrawDevice device) { if (framesLeft <= 0) { OnCinematicsEnd(true); return; } frameProgress += Time.TimeMult; if (frameProgress >= frameDelay) { frameProgress -= frameDelay; framesLeft--; PrepareNextFrame(); } // Render current frame canvas.Begin(device); BatchInfo material = device.RentMaterial(); material.MainTexture = videoTexture; canvas.State.SetMaterial(material); Vector2 targetSize = device.TargetSize; float ratioTarget = targetSize.Y / targetSize.X; float ratioSource = (float)height / width; float ratio = MathF.Clamp(ratioTarget, ratioSource - 0.16f, ratioSource); float fillHeight = targetSize.X * ratio; float yOffset = (targetSize.Y - fillHeight) * 0.5f; canvas.FillRect(0, yOffset, targetSize.X, fillHeight); canvas.End(); }
public bool AddFastFire(int count) { const int FastFireLimit = 9; int current = (weaponUpgrades[(int)WeaponType.Blaster] >> 1); if (current >= FastFireLimit) { return(false); } current = MathF.Min(current + count, FastFireLimit); weaponUpgrades[(int)WeaponType.Blaster] = (byte)((weaponUpgrades[(int)WeaponType.Blaster] & 0x1) | (current << 1)); PlaySound("PickupAmmo"); #if MULTIPLAYER && SERVER ((LevelHandler)levelHandler).OnPlayerRefreshWeaponUpgrades(this, WeaponType.Blaster, weaponUpgrades[(int)WeaponType.Blaster]); #endif return(true); }
public override void OnFixedUpdate(float timeMult) { base.OnFixedUpdate(timeMult); if (frozenTimeLeft > 0) { return; } if (canJump) { if (MathF.Abs(speedX) > float.Epsilon && !CanMoveToPosition(speedX * 4, 0)) { SetTransition(AnimState.TransitionWithdraw, false, delegate { HandleTurn(true); }); isTurning = true; canHurtPlayer = false; speedX = 0; PlaySound("Withdraw", 0.4f); } } if (!isTurning && !isWithdrawn && !isAttacking) { AABB aabb = AABBInner + new Vector2(speedX * 32, 0); if (levelHandler.TileMap.IsTileEmpty(ref aabb, true)) { foreach (Player player in levelHandler.GetCollidingPlayers(aabb + new Vector2(speedX * 32, 0))) { if (!player.IsInvulnerable) { Attack(); break; } } } } }
/// <summary> /// Updates the <see cref="CurrentFrame"/>, <see cref="NextFrame"/> and <see cref="CurrentFrameProgress"/> properties immediately. /// This is called implicitly once each frame before drawing, so you don't normally call this. However, when changing animation /// parameters and requiring updated animation frame data immediately, this could be helpful. /// </summary> public void UpdateVisibleFrames() { // Calculate visible frames curAnimFrame = 0; nextAnimFrame = 0; curAnimFrameFade = 0.0f; if (animFrameCount > 0 && animDuration > 0) { // Calculate currently visible frame float frameTemp = animFrameCount * animTime / animDuration; curAnimFrame = (int)frameTemp; // Normalize current frame when exceeding anim duration if (animLoopMode == LoopMode.Once || animLoopMode == LoopMode.FixedSingle) { curAnimFrame = MathF.Clamp(curAnimFrame, 0, animFrameCount - 1); } else { curAnimFrame = MathF.NormalizeVar(curAnimFrame, 0, animFrameCount); } // Calculate second frame and fade value curAnimFrameFade = frameTemp - (int)frameTemp; if (animLoopMode == LoopMode.Loop) { nextAnimFrame = MathF.NormalizeVar(curAnimFrame + 1, 0, animFrameCount); } else { nextAnimFrame = curAnimFrame + 1; } } curAnimFrame = animFirstFrame + MathF.Clamp(curAnimFrame, 0, animFrameCount - 1); nextAnimFrame = animFirstFrame + MathF.Clamp(nextAnimFrame, 0, animFrameCount - 1); }
public override void OnUpdate() { if (animation < 1f) { animation = Math.Min(animation + Time.TimeMult * 0.016f, 1f); } if (ControlScheme.MenuActionHit(PlayerActions.Fire)) { if (levelList.Count > 0) { api.PlaySound("MenuSelect", 0.5f); api.SwitchToSection(new StartGameOptionsSection(levelList[selectedIndex].EpisodeName, levelList[selectedIndex].LevelName, null)); } } else if (ControlScheme.MenuActionHit(PlayerActions.Menu)) { api.PlaySound("MenuSelect", 0.5f); api.LeaveSection(this); } if (levelList.Count > 1) { if (ControlScheme.MenuActionPressed(PlayerActions.Up)) { if (animation >= 1f - (pressedCount * 0.05f) || ControlScheme.MenuActionHit(PlayerActions.Up)) { api.PlaySound("MenuSelect", 0.4f); animation = 0f; if (selectedIndex > 0) { selectedIndex--; if (selectedIndex < scrollOffset) { scrollOffset = selectedIndex; } } else { selectedIndex = levelList.Count - 1; scrollOffset = Math.Max(0, selectedIndex - (maxVisibleItems - 1)); } pressedCount = Math.Min(pressedCount + 4, 19); } } else if (ControlScheme.MenuActionPressed(PlayerActions.Down)) { if (animation >= 1f - (pressedCount * 0.05f) || ControlScheme.MenuActionHit(PlayerActions.Down)) { api.PlaySound("MenuSelect", 0.4f); animation = 0f; if (selectedIndex < levelList.Count - 1) { selectedIndex++; if (selectedIndex >= scrollOffset + maxVisibleItems) { scrollOffset = selectedIndex - (maxVisibleItems - 1); } } else { selectedIndex = 0; scrollOffset = 0; } pressedCount = Math.Min(pressedCount + 4, 19); } } else { pressedCount = 0; } if (DualityApp.Keyboard.KeyHit(Key.PageUp)) { api.PlaySound("MenuSelect", 0.4f); animation = 0f; selectedIndex = MathF.Max(0, selectedIndex - maxVisibleItems); if (selectedIndex < scrollOffset) { scrollOffset = selectedIndex; } } else if (DualityApp.Keyboard.KeyHit(Key.PageDown)) { api.PlaySound("MenuSelect", 0.4f); animation = 0f; selectedIndex = MathF.Min(levelList.Count - 1, selectedIndex + maxVisibleItems); if (selectedIndex >= scrollOffset + maxVisibleItems) { scrollOffset = selectedIndex - (maxVisibleItems - 1); } } } }
public override void OnPaint(Canvas canvas, Rect view) { IDrawDevice device = canvas.DrawDevice; Vector2 center = device.TargetSize * 0.5f; const float topLine = 96f; float bottomLine = device.TargetSize.Y - 42; api.DrawMaterial("MenuDim", center.X, (topLine + bottomLine) * 0.5f, Alignment.Center, ColorRgba.White, 55f, (bottomLine - topLine) * 0.063f, new Rect(0f, 0.3f, 1f, 0.4f)); int charOffset = 0; if (levelList.Count > 0) { const float itemSpacing = 17f; float topItem = topLine - 4f; float bottomItem = bottomLine - 10f; float contentHeight = bottomItem - topItem; float maxVisibleItemsFloat = (contentHeight / itemSpacing); maxVisibleItems = (int)maxVisibleItemsFloat; float currentItem = topItem + itemSpacing + (maxVisibleItemsFloat - maxVisibleItems) * 0.5f * itemSpacing; // ToDo: ... float column2 = device.TargetSize.X * 0.55f; float sx = column2 * 1.52f; float column1 = column2 * 0.36f + view.X; column2 *= 1.1f; for (int i = 0; i < maxVisibleItems; i++) { int idx = i + scrollOffset; if (idx >= levelList.Count) { break; } if (selectedIndex == idx) { charOffset = 0; float xMultiplier = levelList[idx].DisplayName.Length * 0.5f; float easing = Ease.OutElastic(animation); float x = column1 + xMultiplier - easing * xMultiplier; float size = 0.7f + easing * 0.12f; // Column 2 api.DrawStringShadow(ref charOffset, levelList[idx].LevelName, column2, currentItem, Alignment.Left, new ColorRgba(0.48f, 0.5f), 0.8f, 0.4f, 1f, 1f, 8f, charSpacing: 0.88f); // Column 1 api.DrawStringShadow(ref charOffset, levelList[idx].DisplayName, x, currentItem, Alignment.Left, null, size, 0.4f, 1f, 1f, 8f, charSpacing: 0.88f); // Column 0 api.DrawStringShadow(ref charOffset, levelList[idx].Icon, column1 - 16f, currentItem, Alignment.Right, new ColorRgba(0.48f, 0.5f), size, 0.4f, 1f, 1f, 8f, charSpacing: 0.68f); } else { // Column 2 api.DrawString(ref charOffset, levelList[idx].LevelName, column2, currentItem, Alignment.Left, ColorRgba.TransparentBlack, 0.7f); // Column 1 api.DrawString(ref charOffset, levelList[idx].DisplayName, column1, currentItem, Alignment.Left, ColorRgba.TransparentBlack, 0.7f); // Column 0 api.DrawString(ref charOffset, levelList[idx].Icon, column1 - 16f, currentItem, Alignment.Right, ColorRgba.TransparentBlack, 0.7f, charSpacing: 0.7f); } currentItem += itemSpacing; } // Scrollbar if (levelList.Count > maxVisibleItems) { const float sw = 3f; float sy = ((float)scrollOffset / levelList.Count) * 18f * maxVisibleItems + topLine; float sh = ((float)maxVisibleItems / levelList.Count) * 16f * maxVisibleItems; BatchInfo mat1 = device.RentMaterial(); mat1.Technique = DrawTechnique.Alpha; mat1.MainColor = new ColorRgba(0f, 0f, 0f, 0.28f); canvas.State.SetMaterial(mat1); canvas.FillRect(sx + 1f, sy + 1f, sw, sh); BatchInfo mat2 = device.RentMaterial(); mat2.Technique = DrawTechnique.Alpha; mat2.MainColor = new ColorRgba(0.8f, 0.8f, 0.8f, 0.5f); canvas.State.SetMaterial(mat2); canvas.FillRect(sx, sy, sw, sh); } // Loading if (isLoadingAnimation > 0f) { if (!isLoading) { isLoadingAnimation -= Time.TimeMult * 0.03f; } else { isLoadingAnimation = 1f; } float loadingX = center.X - 50f; float loadingY = center.Y; float startAngle = (float)(Time.GameTimer.TotalSeconds * 6.0f); float time = (float)(Time.GameTimer.TotalSeconds * 1.3f) % 2f; bool reverse = (time >= 1f); if (reverse) { time -= 1f; } float timeCubed = MathF.Pow(time, 3); float timeQuad = MathF.Pow(time, 4); float timeQuint = MathF.Pow(time, 5); float endAngle; if (reverse) { endAngle = startAngle + MathF.TwoPi * (1 - ((6 * timeQuint) + (-15 * timeQuad) + (10 * timeCubed))); } else { endAngle = startAngle + MathF.TwoPi * ((6 * timeQuint) + (-15 * timeQuad) + (10 * timeCubed)); } const float r1 = 7f; const float r2 = r1 - 0.4f; const float r3 = r2 - 0.4f; const float r4 = r3 - 0.4f; api.DrawMaterial("MenuDim", loadingX + 50f, loadingY, Alignment.Center, new ColorRgba(1f, 0.7f * isLoadingAnimation), 40f, 10f); BatchInfo mat1 = device.RentMaterial(); mat1.Technique = DrawTechnique.Alpha; mat1.MainColor = new ColorRgba(0f, 0.2f * isLoadingAnimation); canvas.State.SetMaterial(mat1); canvas.DrawCircleSegment(loadingX + 1.6f, loadingY + 1.6f, r1, startAngle, endAngle); canvas.DrawCircleSegment(loadingX + 1.6f, loadingY + 1.6f, r2, startAngle, endAngle); canvas.DrawCircleSegment(loadingX + 1.6f, loadingY + 1.6f, r3, startAngle, endAngle); canvas.DrawCircleSegment(loadingX + 1.6f, loadingY + 1.6f, r4, startAngle, endAngle); BatchInfo mat2 = device.RentMaterial(); mat2.Technique = DrawTechnique.Alpha; mat2.MainColor = new ColorRgba(0.95f, 0.8f * isLoadingAnimation); canvas.State.SetMaterial(mat2); canvas.DrawCircleSegment(loadingX, loadingY, r1, startAngle, endAngle); canvas.DrawCircleSegment(loadingX, loadingY, r2, startAngle, endAngle); canvas.DrawCircleSegment(loadingX, loadingY, r3, startAngle, endAngle); canvas.DrawCircleSegment(loadingX, loadingY, r4, startAngle, endAngle); api.DrawStringShadow(ref charOffset, "loading".T(), loadingX + r1 + 10f, loadingY + 2f, Alignment.Left, new ColorRgba(0.48f, 0.5f * isLoadingAnimation), 0.8f, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.88f); } } else { api.DrawStringShadow(ref charOffset, "menu/play custom/single/empty".T(), center.X, center.Y, Alignment.Center, new ColorRgba(0.62f, 0.44f, 0.34f, 0.5f), 0.9f, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.88f); } api.DrawMaterial("MenuLine", 0, center.X, topLine, Alignment.Center, ColorRgba.White, 1.6f); api.DrawMaterial("MenuLine", 1, center.X, bottomLine, Alignment.Center, ColorRgba.White, 1.6f); }
public override void OnDraw(Canvas canvas, ref Vector2 pos, bool focused, float animation) { int charOffset = 0; if (focused) { float size = 0.5f + Ease.OutElastic(animation) * 0.6f; api.DrawMaterial("MenuGlow", pos.X, pos.Y, Alignment.Center, ColorRgba.White.WithAlpha(0.4f * size), (title.Length + 3) * 0.5f * size, 4f * size); if (description != null) { api.DrawMaterial("MenuGlow", pos.X, pos.Y + 20f, Alignment.Center, ColorRgba.White.WithAlpha(0.3f * MathF.Min(1f, animation * 6f)), (description.Length + 3) * 0.4f, 3f); api.DrawString(ref charOffset, description, pos.X, pos.Y + 20f, Alignment.Center, new ColorRgba(0.44f, 0.42f, 0.4f, 0.5f), 0.7f); } api.DrawStringShadow(ref charOffset, title, pos.X, pos.Y, Alignment.Center, null, size, 0.7f, 1.1f, 1.1f, charSpacing: 0.9f); } else if (!enabled) { api.DrawString(ref charOffset, title, pos.X, pos.Y, Alignment.Center, new ColorRgba(0.4f, 0.3f), 0.9f); } else { api.DrawString(ref charOffset, description, pos.X, pos.Y + 20f, Alignment.Center, new ColorRgba(0.44f, 0.42f, 0.4f, 0.5f), 0.7f); api.DrawString(ref charOffset, title, pos.X, pos.Y, Alignment.Center, ColorRgba.TransparentBlack, 0.9f); } if (description != null) { pos.Y += 55f; } else { pos.Y += 40f; } }
public override void OnPaint(Canvas canvas, Rect view) { IDrawDevice device = canvas.DrawDevice; Vector2 center = device.TargetSize * 0.5f; const float topLine = 131f; float bottomLine = device.TargetSize.Y - 42; api.DrawMaterial("MenuDim", center.X, (topLine + bottomLine) * 0.5f, Alignment.Center, ColorRgba.White, 55f, (bottomLine - topLine) * 0.063f, new Rect(0f, 0.3f, 1f, 0.4f)); api.DrawMaterial("MenuLine", 0, center.X, topLine, Alignment.Center, ColorRgba.White, 1.6f); api.DrawMaterial("MenuLine", 1, center.X, bottomLine, Alignment.Center, ColorRgba.White, 1.6f); int charOffset = 0; api.DrawStringShadow(ref charOffset, "menu/play story/title".T(), center.X, 110f, Alignment.Center, new ColorRgba(0.5f, 0.5f), 0.9f, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.88f); if (episodes.Count > 0) { float topItem = topLine - 5f; float bottomItem = bottomLine + 5f; float contentHeight = bottomItem - topItem; float itemSpacing = contentHeight / (episodes.Count + 1); topItem += itemSpacing; float topItemSelected = 0f; for (int i = 0; i < episodes.Count; i++) { if (selectedIndex == i) { topItemSelected = topItem; } else { if (episodes[i].IsAvailable) { api.DrawString(ref charOffset, episodes[i].Episode.Name, center.X, topItem, Alignment.Center, ColorRgba.TransparentBlack, 0.9f); } else { api.DrawString(ref charOffset, episodes[i].Episode.Name, center.X, topItem, Alignment.Center, new ColorRgba(0.4f, 0.4f), 0.9f); } } topItem += itemSpacing; } // Selected item last float expandedAnimation2 = Math.Min(expandedAnimation * 6f, 1f); float expandedAnimation3 = (expandedAnimation2 * expandedAnimation2 * (3.0f - 2.0f * expandedAnimation2)); float size = 0.5f + Ease.OutElastic(selectAnimation) * 0.5f + (1f - expandedAnimation3) * 0.2f; if (episodes[selectedIndex].IsAvailable) { if (episodes[selectedIndex].Logo.IsAvailable) { api.DrawString(ref charOffset, episodes[selectedIndex].Episode.Name, center.X, topItemSelected, Alignment.Center, new ColorRgba(0.44f, 0.5f * MathF.Max(0f, 1f - selectAnimation * 2f)), 0.9f - selectAnimation * 0.5f); ContentRef <Material> logo = episodes[selectedIndex].Logo; Texture texture = logo.Res.MainTexture.Res; Vector2 originPos = new Vector2(center.X, topItemSelected); Vector2 logoSize = new Vector2(texture.InternalWidth * size, texture.InternalHeight * size); Alignment.Center.ApplyTo(ref originPos, logoSize); ColorRgba logoColor = ColorRgba.White.WithAlpha(1f - expandedAnimation3 * 0.5f); canvas.State.SetMaterial(logo); canvas.State.ColorTint = logoColor; canvas.FillRect(originPos.X, originPos.Y, texture.InternalWidth * size, texture.InternalHeight * size); if (episodes[selectedIndex].IsComplete) { api.DrawMaterial("EpisodeComplete", originPos.X + logoSize.X * 0.7f, originPos.Y + logoSize.Y * 0.4f, Alignment.TopLeft, logoColor, size, size); } if (episodes[selectedIndex].CanContinue) { float moveX = expandedAnimation3 * -24f; api.DrawString(ref charOffset, ">", center.X + 80f + moveX, topItemSelected, Alignment.Right, new ColorRgba(0.5f, 0.5f * MathF.Min(1f, 0.4f + selectAnimation)), 0.8f, charSpacing: 0.9f); if (expanded) { float expandedAnimation4 = Ease.OutElastic(expandedAnimation) * 0.8f; api.DrawStringShadow(ref charOffset, "menu/play story/restart".T(), center.X + 110f, topItemSelected, Alignment.Center, new ColorRgba(0.62f, 0.44f, 0.34f, 0.5f * MathF.Min(1f, 0.4f + expandedAnimation3)), expandedAnimation4, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.8f); } } } else { api.DrawStringShadow(ref charOffset, episodes[selectedIndex].Episode.Name, center.X, topItemSelected, Alignment.Center, null, size, charSpacing: 0.9f); } } else { api.DrawString(ref charOffset, episodes[selectedIndex].Episode.Name, center.X, topItemSelected, Alignment.Center, new ColorRgba(0.4f, MathF.Max(0.3f, 0.4f - selectAnimation * 0.4f)), MathF.Max(0.7f, 0.9f - selectAnimation * 0.6f)); int index = episodes.IndexOfFirst(entry => entry.Episode.Token == episodes[selectedIndex].Episode.PreviousEpisode); Episode previousEpisode; if (index == -1) { previousEpisode = null; } else { previousEpisode = episodes[index].Episode; } string info; if (previousEpisode == null) { info = "menu/play story/locked".T(); } else { info = "menu/play story/locked prev".T(previousEpisode.Name); } api.DrawStringShadow(ref charOffset, info, center.X, topItemSelected, Alignment.Center, new ColorRgba(0.66f, 0.42f, 0.32f, MathF.Min(0.5f, 0.2f + 2f * selectAnimation)), 0.7f * size, charSpacing: 0.9f); } } else { api.DrawStringShadow(ref charOffset, "menu/play story/empty".T(), center.X, center.Y, Alignment.Center, new ColorRgba(0.62f, 0.44f, 0.34f, 0.5f), 0.9f, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.88f); } }
private void DrawLayer(IDrawDevice device, ref TileMapLayer layer, int cacheIndex) { if (!layer.Visible) { return; } Vector2 viewSize = device.TargetSize; Vector3 viewCenter = device.ViewerPos; Point2 tileCount = new Point2(layer.LayoutWidth, layer.Layout.Length / layer.LayoutWidth); Vector2 tileSize = new Vector2(tileset.TileSize, tileset.TileSize); // Update offsets for moving layers if (MathF.Abs(layer.AutoSpeedX) > 0) { layer.OffsetX += layer.AutoSpeedX * Time.TimeMult; if (layer.RepeatX) { if (layer.AutoSpeedX > 0) { while (layer.OffsetX > (tileCount.X * 32)) { layer.OffsetX -= (tileCount.X * 32); } } else { while (layer.OffsetX < 0) { layer.OffsetX += (tileCount.X * 32); } } } } if (MathF.Abs(layer.AutoSpeedY) > 0) { layer.OffsetY += layer.AutoSpeedY * Time.TimeMult; if (layer.RepeatY) { if (layer.AutoSpeedY > 0) { while (layer.OffsetY > (tileCount.Y * 32)) { layer.OffsetY -= (tileCount.Y * 32); } } else { while (layer.OffsetY < 0) { layer.OffsetY += (tileCount.Y * 32); } } } } // Get current layer offsets and speeds float loX = layer.OffsetX; float loY = layer.OffsetY - (layer.UseInherentOffset ? (viewSize.Y - 200) / 2 : 0); // Find out coordinates for a tile from outside the boundaries from topleft corner of the screen float x1 = viewCenter.X - 70 - (viewSize.X * 0.5f); float y1 = viewCenter.Y - 70 - (viewSize.Y * 0.5f); if (layer.BackgroundStyle != BackgroundStyle.Plain && tileCount.Y == 8 && tileCount.X == 8) { const float PerspectiveSpeedX = 0.4f; const float PerspectiveSpeedY = 0.16f; RenderTexturedBackground(device, ref layer, cacheIndex, (x1 * PerspectiveSpeedX + loX), (y1 * PerspectiveSpeedY + loY)); } else { // Figure out the floating point offset from the calculated coordinates and the actual tile // corner coordinates float xt = TranslateCoordinate(x1, layer.SpeedX, loX, false, viewSize.Y, viewSize.X); float yt = TranslateCoordinate(y1, layer.SpeedY, loY, true, viewSize.Y, viewSize.X); float remX = xt % 32f; float remY = yt % 32f; // Calculate the index (on the layer map) of the first tile that needs to be drawn to the // position determined earlier int tileX, tileY, tileAbsX, tileAbsY; // Get the actual tile coords on the layer layout if (xt > 0) { tileAbsX = (int)Math.Floor(xt / 32f); tileX = tileAbsX % tileCount.X; } else { tileAbsX = (int)Math.Ceiling(xt / 32f); tileX = tileAbsX % tileCount.X; while (tileX < 0) { tileX += tileCount.X; } } if (yt > 0) { tileAbsY = (int)Math.Floor(yt / 32f); tileY = tileAbsY % tileCount.Y; } else { tileAbsY = (int)Math.Ceiling(yt / 32f); tileY = tileAbsY % tileCount.Y; while (tileY < 0) { tileY += tileCount.Y; } } // update x1 and y1 with the remainder so that we start at the tile boundary // minus 1 because indices are updated in the beginning of the loops x1 -= remX - 32f; y1 -= remY - 32f; // Save the tile Y at the left border so that we can roll back to it at the start of // every row iteration int tileYs = tileY; // Calculate the last coordinates we want to draw to float x3 = x1 + 100 + viewSize.X; float y3 = y1 + 100 + viewSize.Y; Material material = null; Texture texture = null; ColorRgba mainColor = ColorRgba.White; // Reserve the required space for vertex data in our locally cached buffer int neededVertices = (int)((((x3 - x1) / 32) + 1) * (((y3 - y1) / 32) + 1) * 4); if (cachedVertices == null || cachedVertices.Length < neededVertices) { cachedVertices = new VertexC1P3T2[neededVertices]; } int vertexIndex = 0; int tile_xo = -1; for (float x2 = x1; x2 < x3; x2 += 32) { tileX = (tileX + 1) % tileCount.X; tile_xo++; if (!layer.RepeatX) { // If the current tile isn't in the first iteration of the layer horizontally, don't draw this column if (tileAbsX + tile_xo + 1 < 0 || tileAbsX + tile_xo + 1 >= tileCount.X) { continue; } } tileY = tileYs; int tile_yo = -1; for (float y2 = y1; y2 < y3; y2 += 32) { tileY = (tileY + 1) % tileCount.Y; tile_yo++; LayerTile tile = layer.Layout[tileX + tileY * layer.LayoutWidth]; if (!layer.RepeatY) { // If the current tile isn't in the first iteration of the layer vertically, don't draw it if (tileAbsY + tile_yo + 1 < 0 || tileAbsY + tile_yo + 1 >= tileCount.Y) { continue; } } Point2 offset; bool isFlippedX, isFlippedY; if (tile.IsAnimated) { if (tile.TileID < animatedTiles.Count) { offset = animatedTiles[tile.TileID].CurrentTile.MaterialOffset; isFlippedX = (animatedTiles[tile.TileID].CurrentTile.IsFlippedX != tile.IsFlippedX); isFlippedY = (animatedTiles[tile.TileID].CurrentTile.IsFlippedY != tile.IsFlippedY); //mainColor.A = tile.MaterialAlpha; mainColor.A = animatedTiles[tile.TileID].CurrentTile.MaterialAlpha; } else { continue; } } else { offset = tile.MaterialOffset; isFlippedX = tile.IsFlippedX; isFlippedY = tile.IsFlippedY; mainColor.A = tile.MaterialAlpha; } if (material != tile.Material) { // Submit all the vertices as one draw batch device.AddVertices( material, VertexMode.Quads, cachedVertices, 0, vertexIndex); vertexIndex = 0; material = tile.Material.Res; texture = material.MainTexture.Res; } Rect uvRect = new Rect( offset.X * texture.UVRatio.X / texture.ContentWidth, offset.Y * texture.UVRatio.Y / texture.ContentHeight, tileset.TileSize * texture.UVRatio.X / texture.ContentWidth, tileset.TileSize * texture.UVRatio.Y / texture.ContentHeight ); // ToDo: Flip normal map somehow if (isFlippedX) { uvRect.X += uvRect.W; uvRect.W *= -1; } if (isFlippedY) { uvRect.Y += uvRect.H; uvRect.H *= -1; } Vector3 renderPos = new Vector3(x2, y2, layer.Depth); renderPos.X = MathF.Round(renderPos.X); renderPos.Y = MathF.Round(renderPos.Y); if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2) { renderPos.X += 0.5f; } if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2) { renderPos.Y += 0.5f; } cachedVertices[vertexIndex + 0].Pos.X = renderPos.X; cachedVertices[vertexIndex + 0].Pos.Y = renderPos.Y; cachedVertices[vertexIndex + 0].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 0].TexCoord.X = uvRect.X; cachedVertices[vertexIndex + 0].TexCoord.Y = uvRect.Y; cachedVertices[vertexIndex + 0].Color = mainColor; cachedVertices[vertexIndex + 1].Pos.X = renderPos.X; cachedVertices[vertexIndex + 1].Pos.Y = renderPos.Y + tileSize.Y; cachedVertices[vertexIndex + 1].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 1].TexCoord.X = uvRect.X; cachedVertices[vertexIndex + 1].TexCoord.Y = uvRect.Y + uvRect.H; cachedVertices[vertexIndex + 1].Color = mainColor; cachedVertices[vertexIndex + 2].Pos.X = renderPos.X + tileSize.X; cachedVertices[vertexIndex + 2].Pos.Y = renderPos.Y + tileSize.Y; cachedVertices[vertexIndex + 2].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 2].TexCoord.X = uvRect.X + uvRect.W; cachedVertices[vertexIndex + 2].TexCoord.Y = uvRect.Y + uvRect.H; cachedVertices[vertexIndex + 2].Color = mainColor; cachedVertices[vertexIndex + 3].Pos.X = renderPos.X + tileSize.X; cachedVertices[vertexIndex + 3].Pos.Y = renderPos.Y; cachedVertices[vertexIndex + 3].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 3].TexCoord.X = uvRect.X + uvRect.W; cachedVertices[vertexIndex + 3].TexCoord.Y = uvRect.Y; cachedVertices[vertexIndex + 3].Color = mainColor; vertexIndex += 4; } } // Submit all the vertices as one draw batch device.AddVertices( material, VertexMode.Quads, cachedVertices, 0, vertexIndex); } }
public unsafe void DrawString(ref int charOffset, string text, float x, float y, Alignment alignment, ColorRgba? color = null, float scale = 1f, float angleOffset = 0f, float varianceX = 4f, float varianceY = 4f, float speed = 4f, float charSpacing = 1f, float lineSpacing = 1f) { const int MaxColorizeIndex = 7; if (string.IsNullOrEmpty(text)) { return; } float phase = (float)Time.GameTimer.TotalSeconds * speed; bool hasColor = false; // Pre-compute text size //int lines = 1; float totalWidth = 0f, lastWidth = 0f, totalHeight = 0f; float charSpacingPre = charSpacing; float scalePre = scale; for (int i = 0; i < text.Length; i++) { if (text[i] == '\n') { if (lastWidth < totalWidth) { lastWidth = totalWidth; } totalWidth = 0f; totalHeight += (charHeight * scale * lineSpacing); //lines++; continue; } else if (text[i] == '\f' && text[i + 1] == '[') { i += 2; int formatIndex = i; while (text[i] != ']') { i++; } if (text[formatIndex + 1] == ':') { int paramInt; switch (text[formatIndex]) { case 'c': // Color hasColor = true; break; case 's': // Scale if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { scalePre = paramInt * 0.01f; } break; case 'w': // Char spacing if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { charSpacingPre = paramInt * 0.01f; } break; } } continue; } Rect uvRect; if (!unicodeChars.TryGetValue(text[i], out uvRect)) { byte ascii = (byte)text[i]; if (ascii < 128) { uvRect = asciiChars[ascii]; } else { uvRect = new Rect(); } } if (uvRect.W > 0 && uvRect.H > 0) { totalWidth += (uvRect.W + baseSpacing) * charSpacingPre * scalePre; } } if (lastWidth < totalWidth) { lastWidth = totalWidth; } totalHeight += (charHeight * scale * lineSpacing); VertexC1P3T2[] vertexData = canvas.RentVertices(text.Length * 4); // Set default material bool colorize, allowColorChange; ContentRef<Material> material; ColorRgba mainColor; if (color.HasValue) { mainColor = color.Value; if (mainColor == ColorRgba.TransparentBlack) { if (hasColor) { material = materialColor; mainColor = new ColorRgba(0.46f, 0.46f, 0.4f, 0.5f); } else { material = materialPlain; mainColor = ColorRgba.White; } } else { material = materialColor; } colorize = false; if (mainColor.R == 0 && mainColor.G == 0 && mainColor.B == 0) { allowColorChange = false; } else { allowColorChange = true; } } else { material = materialColor; mainColor = ColorRgba.White; colorize = true; allowColorChange = false; } Vector2 uvRatio = new Vector2( 1f / materialPlain.Res.MainTexture.Res.ContentWidth, 1f / materialPlain.Res.MainTexture.Res.ContentHeight ); int vertexIndex = 0; Vector2 originPos = new Vector2(x, y); alignment.ApplyTo(ref originPos, new Vector2(lastWidth, totalHeight)); float lineStart = originPos.X; for (int i = 0; i < text.Length; i++) { if (text[i] == '\n') { // New line originPos.X = lineStart; originPos.Y += (charHeight * scale * lineSpacing); continue; } else if (text[i] == '\f' && text[i + 1] == '[') { // Format i += 2; int formatIndex = i; while (text[i] != ']') { i++; } if (text[formatIndex + 1] == ':') { int paramInt; switch (text[formatIndex]) { case 'c': // Color if (allowColorChange && int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { if (paramInt == -1) { colorize = true; } else { colorize = false; mainColor = colors[paramInt % colors.Length]; } } break; case 's': // Scale if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { scale = paramInt * 0.01f; } break; case 'w': // Char spacing if (int.TryParse(text.Substring(formatIndex + 2, i - (formatIndex + 2)), out paramInt)) { charSpacing = paramInt * 0.01f; } break; default: // Unknown formatting break; } } continue; } Rect uvRect; if (!unicodeChars.TryGetValue(text[i], out uvRect)) { byte ascii = (byte)text[i]; if (ascii < 128) { uvRect = asciiChars[ascii]; } else { uvRect = new Rect(); } } if (uvRect.W > 0 && uvRect.H > 0) { if (colorize) { mainColor = colors[charOffset % MaxColorizeIndex]; } Vector3 pos = new Vector3(originPos); if (angleOffset > 0f) { float currentPhase = (phase + charOffset) * angleOffset * MathF.Pi; if (speed > 0f && charOffset % 2 == 1) { currentPhase = -currentPhase; } pos.X += MathF.Cos(currentPhase) * varianceX * scale; pos.Y += MathF.Sin(currentPhase) * varianceY * scale; } pos.X = MathF.Round(pos.X); pos.Y = MathF.Round(pos.Y); float x2 = MathF.Round(pos.X + uvRect.W * scale); float y2 = MathF.Round(pos.Y + uvRect.H * scale); vertexData[vertexIndex + 0].Pos = pos; vertexData[vertexIndex + 0].TexCoord.X = uvRect.X; vertexData[vertexIndex + 0].TexCoord.Y = uvRect.Y; vertexData[vertexIndex + 0].Color = mainColor; vertexData[vertexIndex + 1].Pos.X = pos.X; vertexData[vertexIndex + 1].Pos.Y = y2; vertexData[vertexIndex + 1].Pos.Z = pos.Z; vertexData[vertexIndex + 1].TexCoord.X = uvRect.X; vertexData[vertexIndex + 1].TexCoord.Y = uvRect.Y + uvRect.H * uvRatio.Y; vertexData[vertexIndex + 1].Color = mainColor; vertexData[vertexIndex + 2].Pos.X = x2; vertexData[vertexIndex + 2].Pos.Y = y2; vertexData[vertexIndex + 2].Pos.Z = pos.Z; vertexData[vertexIndex + 2].TexCoord.X = uvRect.X + uvRect.W * uvRatio.X; vertexData[vertexIndex + 2].TexCoord.Y = uvRect.Y + uvRect.H * uvRatio.Y; vertexData[vertexIndex + 2].Color = mainColor; vertexData[vertexIndex + 3].Pos.X = x2; vertexData[vertexIndex + 3].Pos.Y = pos.Y; vertexData[vertexIndex + 3].Pos.Z = pos.Z; vertexData[vertexIndex + 3].TexCoord.X = uvRect.X + uvRect.W * uvRatio.X; vertexData[vertexIndex + 3].TexCoord.Y = uvRect.Y; vertexData[vertexIndex + 3].Color = mainColor; if (MathF.RoundToInt(canvas.DrawDevice.TargetSize.X) != (MathF.RoundToInt(canvas.DrawDevice.TargetSize.X) / 2) * 2) { float align = 0.5f / canvas.DrawDevice.TargetSize.X; vertexData[vertexIndex + 0].Pos.X += align; vertexData[vertexIndex + 1].Pos.X += align; vertexData[vertexIndex + 2].Pos.X += align; vertexData[vertexIndex + 3].Pos.X += align; } if (MathF.RoundToInt(canvas.DrawDevice.TargetSize.Y) != (MathF.RoundToInt(canvas.DrawDevice.TargetSize.Y) / 2) * 2) { float align = 0.5f * scale / canvas.DrawDevice.TargetSize.Y; vertexData[vertexIndex + 0].Pos.Y += align; vertexData[vertexIndex + 1].Pos.Y += align; vertexData[vertexIndex + 2].Pos.Y += align; vertexData[vertexIndex + 3].Pos.Y += align; } vertexIndex += 4; originPos.X += ((uvRect.W + baseSpacing) * scale * charSpacing); } charOffset++; } charOffset++; // Submit all the vertices as one draw batch canvas.DrawDevice.AddVertices( material, VertexMode.Quads, vertexData, 0, vertexIndex); }
private void RecreateTexturedBackground(TileSet levelTileset, ref TileMapLayer layer) { int w = layer.LayoutWidth; int h = layer.Layout.Length / w; Texture targetTexture; if (cachedTexturedBackground.IsAvailable) { targetTexture = cachedTexturedBackground.Res; } else { targetTexture = new Texture(w * 32, h * 32, TextureSizeMode.NonPowerOfTwo, TextureMagFilter.Linear, TextureMinFilter.Linear, TextureWrapMode.Repeat, TextureWrapMode.Repeat); } using (DrawDevice device = new DrawDevice()) { device.VisibilityMask = VisibilityFlag.AllFlags; device.Projection = ProjectionMode.Screen; using (RenderTarget target = new RenderTarget(AAQuality.Off, false, targetTexture)) { device.Target = target; device.TargetSize = new Vector2(w * 32, h * 32); device.ViewportRect = new Rect(device.TargetSize); device.PrepareForDrawcalls(); // ToDo Material material = levelTileset.GetDefaultTile(0).Material.Res; Texture texture = material.MainTexture.Res; // Reserve the required space for vertex data in our locally cached buffer int neededVertices = 4 * w * h; if (cachedVertices == null || cachedVertices.Length < neededVertices) { cachedVertices = new VertexC1P3T2[neededVertices]; } int vertexIndex = 0; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { LayerTile tile = layer.Layout[x + y * layer.LayoutWidth]; if (tile.IsAnimated) { continue; } Point2 offset = tile.MaterialOffset; bool isFlippedX = tile.IsFlippedX; bool isFlippedY = tile.IsFlippedY; Rect uvRect = new Rect( offset.X * texture.UVRatio.X / texture.ContentWidth, offset.Y * texture.UVRatio.Y / texture.ContentHeight, levelTileset.TileSize * texture.UVRatio.X / texture.ContentWidth, levelTileset.TileSize * texture.UVRatio.Y / texture.ContentHeight ); if (isFlippedX) { uvRect.X += uvRect.W; uvRect.W *= -1; } if (isFlippedY) { uvRect.Y += uvRect.H; uvRect.H *= -1; } Vector3 renderPos = new Vector3(x * 32, y * 32, 0); renderPos.X = MathF.Round(renderPos.X); renderPos.Y = MathF.Round(renderPos.Y); if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2) { renderPos.X += 0.5f; } if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2) { renderPos.Y += 0.5f; } Vector2 tileXStep = new Vector2(32, 0); Vector2 tileYStep = new Vector2(0, 32); cachedVertices[vertexIndex + 0].Pos.X = renderPos.X; cachedVertices[vertexIndex + 0].Pos.Y = renderPos.Y; cachedVertices[vertexIndex + 0].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 0].TexCoord.X = uvRect.X; cachedVertices[vertexIndex + 0].TexCoord.Y = uvRect.Y; cachedVertices[vertexIndex + 0].Color = ColorRgba.White; cachedVertices[vertexIndex + 1].Pos.X = renderPos.X + tileYStep.X; cachedVertices[vertexIndex + 1].Pos.Y = renderPos.Y + tileYStep.Y; cachedVertices[vertexIndex + 1].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 1].TexCoord.X = uvRect.X; cachedVertices[vertexIndex + 1].TexCoord.Y = uvRect.Y + uvRect.H; cachedVertices[vertexIndex + 1].Color = ColorRgba.White; cachedVertices[vertexIndex + 2].Pos.X = renderPos.X + tileXStep.X + tileYStep.X; cachedVertices[vertexIndex + 2].Pos.Y = renderPos.Y + tileXStep.Y + tileYStep.Y; cachedVertices[vertexIndex + 2].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 2].TexCoord.X = uvRect.X + uvRect.W; cachedVertices[vertexIndex + 2].TexCoord.Y = uvRect.Y + uvRect.H; cachedVertices[vertexIndex + 2].Color = ColorRgba.White; cachedVertices[vertexIndex + 3].Pos.X = renderPos.X + tileXStep.X; cachedVertices[vertexIndex + 3].Pos.Y = renderPos.Y + tileXStep.Y; cachedVertices[vertexIndex + 3].Pos.Z = renderPos.Z; cachedVertices[vertexIndex + 3].TexCoord.X = uvRect.X + uvRect.W; cachedVertices[vertexIndex + 3].TexCoord.Y = uvRect.Y; cachedVertices[vertexIndex + 3].Color = ColorRgba.White; vertexIndex += 4; } } device.AddVertices(material, VertexMode.Quads, cachedVertices, 0, vertexIndex); device.Render(); } } cachedTexturedBackground = targetTexture; }
protected void PrepareVerticesSmooth(ref VertexC1P3T4A1[] vertices, IDrawDevice device, float curAnimFrameFade, ColorRgba mainClr, Rect uvRect, Rect uvRectNext) { Vector3 pos = this.gameobj.Transform.Pos; Vector2 xDot, yDot; MathF.GetTransformDotVec(this.gameobj.Transform.Angle, this.gameobj.Transform.Scale, out xDot, out yDot); Rect rectTemp = rect.Transformed(gameobj.Transform.Scale, gameobj.Transform.Scale); Vector2 edge1 = rectTemp.TopLeft; Vector2 edge2 = rectTemp.BottomLeft; Vector2 edge3 = rectTemp.BottomRight; Vector2 edge4 = rectTemp.TopRight; MathF.TransformDotVec(ref edge1, ref xDot, ref yDot); MathF.TransformDotVec(ref edge2, ref xDot, ref yDot); MathF.TransformDotVec(ref edge3, ref xDot, ref yDot); MathF.TransformDotVec(ref edge4, ref xDot, ref yDot); float left = uvRect.X; float right = uvRect.RightX; float top = uvRect.Y; float bottom = uvRect.BottomY; float nextLeft = uvRectNext.X; float nextRight = uvRectNext.RightX; float nextTop = uvRectNext.Y; float nextBottom = uvRectNext.BottomY; if ((flipMode & FlipMode.Horizontal) != FlipMode.None) { edge1.X = -edge1.X; edge2.X = -edge2.X; edge3.X = -edge3.X; edge4.X = -edge4.X; } if ((flipMode & FlipMode.Vertical) != FlipMode.None) { edge1.Y = -edge1.Y; edge2.Y = -edge2.Y; edge3.Y = -edge3.Y; edge4.Y = -edge4.Y; } if (vertices == null /*|| vertices.Length != 4*/) { vertices = new VertexC1P3T4A1[4]; } vertices[0].Pos.X = pos.X + edge1.X; vertices[0].Pos.Y = pos.Y + edge1.Y; vertices[0].Pos.Z = pos.Z + VertexZOffset; vertices[0].TexCoord.X = left; vertices[0].TexCoord.Y = top; vertices[0].TexCoord.Z = nextLeft; vertices[0].TexCoord.W = nextTop; vertices[0].Color = mainClr; vertices[0].Attrib = curAnimFrameFade; vertices[1].Pos.X = pos.X + edge2.X; vertices[1].Pos.Y = pos.Y + edge2.Y; vertices[1].Pos.Z = pos.Z + VertexZOffset; vertices[1].TexCoord.X = left; vertices[1].TexCoord.Y = bottom; vertices[1].TexCoord.Z = nextLeft; vertices[1].TexCoord.W = nextBottom; vertices[1].Color = mainClr; vertices[1].Attrib = curAnimFrameFade; vertices[2].Pos.X = pos.X + edge3.X; vertices[2].Pos.Y = pos.Y + edge3.Y; vertices[2].Pos.Z = pos.Z + VertexZOffset; vertices[2].TexCoord.X = right; vertices[2].TexCoord.Y = bottom; vertices[2].TexCoord.Z = nextRight; vertices[2].TexCoord.W = nextBottom; vertices[2].Color = mainClr; vertices[2].Attrib = curAnimFrameFade; vertices[3].Pos.X = pos.X + edge4.X; vertices[3].Pos.Y = pos.Y + edge4.Y; vertices[3].Pos.Z = pos.Z + VertexZOffset; vertices[3].TexCoord.X = right; vertices[3].TexCoord.Y = top; vertices[3].TexCoord.Z = nextRight; vertices[3].TexCoord.W = nextTop; vertices[3].Color = mainClr; vertices[3].Attrib = curAnimFrameFade; if (pixelGrid) { vertices[0].Pos.X = MathF.Round(vertices[0].Pos.X); vertices[1].Pos.X = MathF.Round(vertices[1].Pos.X); vertices[2].Pos.X = MathF.Round(vertices[2].Pos.X); vertices[3].Pos.X = MathF.Round(vertices[3].Pos.X); if (MathF.RoundToInt(device.TargetSize.X) != (MathF.RoundToInt(device.TargetSize.X) / 2) * 2) { vertices[0].Pos.X += 0.5f; vertices[1].Pos.X += 0.5f; vertices[2].Pos.X += 0.5f; vertices[3].Pos.X += 0.5f; } vertices[0].Pos.Y = MathF.Round(vertices[0].Pos.Y); vertices[1].Pos.Y = MathF.Round(vertices[1].Pos.Y); vertices[2].Pos.Y = MathF.Round(vertices[2].Pos.Y); vertices[3].Pos.Y = MathF.Round(vertices[3].Pos.Y); if (MathF.RoundToInt(device.TargetSize.Y) != (MathF.RoundToInt(device.TargetSize.Y) / 2) * 2) { vertices[0].Pos.Y += 0.5f; vertices[1].Pos.Y += 0.5f; vertices[2].Pos.Y += 0.5f; vertices[3].Pos.Y += 0.5f; } } }
protected override void OnFixedUpdate(float timeMult) { collisions.Clear(); api.FindCollisionActorsByAABB(this, AABBInner, ResolveCollisions); Vector3 pos = Transform.Pos; bool found = false; foreach (ActorBase collision in collisions) { // ToDo: This code only works with one player Player player = collision as Player; if (player != null) { if (player != lastPlayer) { if (player.Speed.Y < -0.5f) { continue; } lastPlayer = player; } found = true; Vector3 coords = player.Transform.Pos; int length = bridgePieces.Count; // This marks which bridge piece is under the player and should be positioned // lower than any other piece of the bridge. float lowest = (coords.X - pos.X) / (bridgeWidth * 16f) * length; // This marks the maximum drop in height. // At the middle of the bridge, this is purely the height factor, // which is simply (16 - bridge toughness) multiplied by the length of the bridge. // At other points, the height is scaled by an (arbitrarily chosen) power that // gives a nice curve. // Additionally, the drop is reduced based on the player position so that the // bridge seems to bend somewhat realistically instead of snapping from one position // to another. float drop = Math.Max(0, Math.Min(coords.Y - pos.Y + 32, (1f - MathF.Pow(Math.Abs(2f * lowest / length - 1f), 0.8f)) * heightFactor)); pos.Y = Math.Min(originalY + drop, Math.Max(originalY, coords.Y)); Transform.Pos = pos; // Update the position of each bridge piece. for (int j = 0; j < length; ++j) { Piece piece = bridgePieces[j]; coords = piece.Transform.Pos; if (lowest > 0 && lowest < length) { float dropPiece; if (j <= lowest) { dropPiece = MathF.Pow(j / lowest, 0.6f) * drop; piece.Transform.Angle = dropPiece * 0.006f; } else { dropPiece = MathF.Pow((length - 1 - j) / (length - 1 - lowest), 0.6f) * drop; piece.Transform.Angle = -dropPiece * 0.006f; } coords.Y = originalY + dropPiece; } else { coords.Y = originalY; piece.Transform.Angle = 0f; } piece.Transform.Pos = coords; } } } if (!found) { // The player was not touching the bridge, so reset all pieces to the default height. for (int j = 0; j < bridgePieces.Count; ++j) { Vector3 coords = bridgePieces[j].Transform.Pos; coords.Y = originalY; bridgePieces[j].Transform.Pos = coords; bridgePieces[j].Transform.Angle = 0f; } pos.Y = originalY; Transform.Pos = pos; lastPlayer = null; } }
protected virtual void OnFixedUpdate(float timeMult) { if (currentCarryOver.HasValue) { bool playersReady = true; foreach (Player player in players) { // Exit type is already provided playersReady &= player.OnLevelChanging(ExitType.None); } if (playersReady) { if (levelChangeTimer > 0) { levelChangeTimer -= timeMult; } else { root.ChangeLevel(currentCarryOver.Value); currentCarryOver = null; initState = InitState.Disposed; return; } } } if (difficulty != GameDifficulty.Multiplayer) { if (players.Count > 0) { Vector3 pos = players[0].Transform.Pos; int tx1 = (int)pos.X >> 5; int ty1 = (int)pos.Y >> 5; int tx2 = tx1; int ty2 = ty1; #if ENABLE_SPLITSCREEN for (int i = 1; i < players.Count; i++) { Vector3 pos2 = players[i].Transform.Pos; int tx = (int)pos2.X >> 5; int ty = (int)pos2.Y >> 5; if (tx1 > tx) { tx1 = tx; } else if (tx2 < tx) { tx2 = tx; } if (ty1 > ty) { ty1 = ty; } else if (ty2 < ty) { ty2 = ty; } } #endif // ToDo: Remove this branching #if __ANDROID__ const int ActivateTileRange = 20; #else const int ActivateTileRange = 26; #endif tx1 -= ActivateTileRange; ty1 -= ActivateTileRange; tx2 += ActivateTileRange; ty2 += ActivateTileRange; for (int i = 0; i < actors.Count; i++) { if (actors[i].OnTileDeactivate(tx1 - 2, ty1 - 2, tx2 + 2, ty2 + 2)) { i--; } } eventMap.ActivateEvents(tx1, ty1, tx2, ty2, initState != InitState.Initializing); } eventMap.ProcessGenerators(timeMult); } ResolveCollisions(); // Ambient Light Transition if (ambientLightCurrent != ambientLightTarget) { float step = timeMult * 0.012f; if (MathF.Abs(ambientLightCurrent - ambientLightTarget) < step) { ambientLightCurrent = ambientLightTarget; } else { ambientLightCurrent += step * ((ambientLightTarget < ambientLightCurrent) ? -1 : 1); } } // Weather if (weatherType != WeatherType.None && commonResources.Graphics != null) { // ToDo: Apply weather effect to all other cameras too Vector3 viewPos = cameras[0].Transform.Pos; for (int i = 0; i < weatherIntensity; i++) { TileMap.DebrisCollisionAction collisionAction; if (weatherOutdoors) { collisionAction = TileMap.DebrisCollisionAction.Disappear; } else { collisionAction = (MathF.Rnd.NextFloat() > 0.7f ? TileMap.DebrisCollisionAction.None : TileMap.DebrisCollisionAction.Disappear); } Vector3 debrisPos = viewPos + MathF.Rnd.NextVector3((LevelRenderSetup.TargetSize.X / -2) - 40, (LevelRenderSetup.TargetSize.Y * -2 / 3), MainPlaneZ, LevelRenderSetup.TargetSize.X + 120, LevelRenderSetup.TargetSize.Y, 0); if (weatherType == WeatherType.Rain) { GraphicResource res = commonResources.Graphics["Rain"]; Material material = res.Material.Res; Texture texture = material.MainTexture.Res; float scale = MathF.Rnd.NextFloat(0.4f, 1.1f); float speedX = MathF.Rnd.NextFloat(2.2f, 2.7f) * scale; float speedY = MathF.Rnd.NextFloat(7.6f, 8.6f) * scale; debrisPos.Z = MainPlaneZ * scale; tileMap.CreateDebris(new TileMap.DestructibleDebris { Pos = debrisPos, Size = res.Base.FrameDimensions, Speed = new Vector2(speedX, speedY), Scale = scale, Angle = MathF.Atan2(speedY, speedX), Alpha = 1f, Time = 180f, Material = material, MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)), CollisionAction = collisionAction }); } else { GraphicResource res = commonResources.Graphics["Snow"]; Material material = res.Material.Res; Texture texture = material.MainTexture.Res; float scale = MathF.Rnd.NextFloat(0.4f, 1.1f); float speedX = MathF.Rnd.NextFloat(-1.6f, -1.2f) * scale; float speedY = MathF.Rnd.NextFloat(3f, 4f) * scale; float accel = MathF.Rnd.NextFloat(-0.008f, 0.008f) * scale; debrisPos.Z = MainPlaneZ * scale; tileMap.CreateDebris(new TileMap.DestructibleDebris { Pos = debrisPos, Size = res.Base.FrameDimensions, Speed = new Vector2(speedX, speedY), Acceleration = new Vector2(accel, -MathF.Abs(accel)), Scale = scale, Angle = MathF.Rnd.NextFloat(MathF.TwoPi), AngleSpeed = speedX * 0.02f, Alpha = 1f, Time = 180f, Material = material, MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)), CollisionAction = collisionAction }); } } } // Active Boss if (activeBoss != null && activeBoss.Scene == null) { activeBoss = null; Hud hud = rootObject.GetComponent <Hud>(); if (hud != null) { hud.ActiveBoss = null; } InitLevelChange(ExitType.Normal, null); levelChangeTimer = 300; } if (initState == InitState.Initializing) { initState = InitState.Initialized; } collisionsCountA = 0; collisionsCountB = 0; collisionsCountC = 0; }