using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class Mirror : Wall, IBulletInteractor, IBreakable { [Space(15)] public GameObject scatteredMirror; public bool doReflect = false; public void Break() { Instantiate(scatteredMirror, transform.position, transform.rotation); MapManager.inst.currentMap.RemoveWall(this.mapPos); } public void StartCopy() { StartCoroutine(CopyObjects(PlayerController.inst.currentPlayer)); } public void Interact(Bullet bullet) { if (bullet is FakeBullet) { doReflect = true; //Debug.Log("ldPos: " + ldPos + ", rdPos: " + rdPos + ", dir: " + dir); // Make reflected objects } } /// /// copy objects which reflected by this mirror /// /// transform of shooter IEnumerator CopyObjects(Player _shooter) { Vector2 stPos = _shooter.pos; // position of shooter's cell //Debug.Log("stPos: " + stPos); List parRay = new List { new Pair(PointToParRay(stPos, ldPos, false), PointToParRay(stPos, rdPos, false)) }; //Debug.Log(parRay[0].l + ", " + parRay[0].r); int side, i, reflectSide, mapRange; bool isSameRSide; if (dir) // horizontal, parallel with x { side = (mapPos.y - stPos.y > 0) ? -1 : 1; reflectSide = (mapPos.x - stPos.x > 0) ? 1 : -1; if (isSameRSide = mapPos.x == stPos.x) mapRange = Mathf.RoundToInt(mapPos.x); else mapRange = -reflectSide * (MapManager.inst.currentMap.maxMapSize + 1); i = side > 0 ? Mathf.CeilToInt(mapPos.y) : Mathf.FloorToInt(mapPos.y); } else // vertical, parallel with y { side = (mapPos.x - stPos.x > 0) ? -1 : 1; reflectSide = (mapPos.y - stPos.y > 0) ? 1 : -1; if (isSameRSide = mapPos.y == stPos.y) mapRange = Mathf.RoundToInt(mapPos.y); else mapRange = -reflectSide * (MapManager.inst.currentMap.maxMapSize + 1); i = side > 0 ? Mathf.CeilToInt(mapPos.x) : Mathf.FloorToInt(mapPos.x); } //Debug.Log("side: " + side + ", reflectSide: " + reflectSide + ", i: " + i + ", isSameRSide: " + isSameRSide); //Debug.Log("minRange: " + mapRange); yield return null; // check before reflect (check walls and mirrors) float mirrorPos = (dir ? mapPos.y : mapPos.x); float stPosFix = (dir ? stPos.y : stPos.x); foreach (var wall in MapManager.inst.currentMap.wallGrid) { float wallPos = (dir ? wall.Key.y : wall.Key.x); if (wall.Key != mapPos && (side < 0 ? wallPos < mirrorPos && wallPos > stPosFix : wallPos > mirrorPos && wallPos < stPosFix)) { Pair pair = new Pair(PointToParRay(stPos, wall.Value.ldPos, false), PointToParRay(stPos, wall.Value.rdPos, false)); //Debug.Log(wall.Key); if (IsInRay(parRay, pair)) SubtractRay(parRay, pair); yield return null; } } Dictionary copyFloorGrid = new Dictionary(MapManager.inst.currentMap.floorGrid); Dictionary copyWallGrid = new Dictionary(MapManager.inst.currentMap.wallGrid); Dictionary floorCountGrid = new Dictionary(); foreach (var floor in copyFloorGrid) { floorCountGrid.Add(floor.Key, 0); } // start reflection Debug.Log("Start Reflection"); if (parRay.Count > 0) { Vector2Int frontFloorPos = dir ? new Vector2Int(Mathf.RoundToInt(mapPos.x), Mathf.RoundToInt(mapPos.y + 0.5f * side)) : new Vector2Int(Mathf.RoundToInt(mapPos.x + 0.5f * side), Mathf.RoundToInt(mapPos.y)); if (floorCountGrid.TryGetValue(frontFloorPos, out int frontFloorCount)) { if (frontFloorCount == 0) floorCountGrid[frontFloorPos]++; // have floor } else // no floor on there { floorCountGrid.Add(frontFloorPos, -1); } } for (; Mathf.Abs(i) < (MapManager.inst.currentMap.maxMapSize + 1); i += side) { yield return null; bool anotherSide = false; for (int j = mapRange; Mathf.Abs(j) <= (MapManager.inst.currentMap.maxMapSize + 1); j += reflectSide) { // check floors Vector2Int floorPos = dir ? new Vector2Int(j, i) : new Vector2Int(i, j); Pair floorPair = new Pair( PointToParRay(stPos, floorPos + 0.5f * (dir ? new Vector2(reflectSide, -side) : new Vector2(-side, reflectSide)), true), PointToParRay(stPos, floorPos + 0.5f * (dir ? new Vector2(-reflectSide, side) : new Vector2(side, -reflectSide)), true)); if (IsInRay(parRay, floorPair)) { int floorCount; if (floorCountGrid.TryGetValue(floorPos, out floorCount)) { if (floorCount == 0) floorCountGrid[floorPos]++; // have floor } else // no floor on there { floorCountGrid.Add(floorPos, -1); } } // check walls and copy Vector2 wallPos = dir ? new Vector2(j + 0.5f * reflectSide, i) : new Vector2(i, j + 0.5f * reflectSide); //Debug.Log(wallPos); Vector2 oppoPos = GetOpposite(wallPos); Pair wallPair = dir ? (new Pair(PointToParRay(stPos, wallPos + new Vector2(0, -0.5f), true), PointToParRay(stPos, wallPos + new Vector2(0, 0.5f), true))) : (new Pair(PointToParRay(stPos, wallPos + new Vector2(-0.5f, 0), true), PointToParRay(stPos, wallPos + new Vector2(0.5f, 0), true))); if (IsInRay(parRay, wallPair)) { if (copyWallGrid.ContainsKey(wallPos)) { Wall originWall = copyWallGrid[wallPos]; MapManager.inst.currentMap.CreateWall(oppoPos, originWall.type, false); SubtractRay(parRay, wallPair); } else { if (copyWallGrid.ContainsKey(oppoPos)) MapManager.inst.currentMap.RemoveWall(oppoPos); } } if (isSameRSide && Mathf.Abs(j) == (MapManager.inst.currentMap.maxMapSize + 1)) { if (anotherSide) { reflectSide *= -1; anotherSide = false; break; } else { anotherSide = true; reflectSide *= -1; j = mapRange - reflectSide; } } } float iMid = i + 0.5f * side; // check walls and copy for (int j = mapRange; Mathf.Abs(j) <= (MapManager.inst.currentMap.maxMapSize + 1); j += reflectSide) { Vector2 wallPos = dir ? new Vector2(j, iMid) : new Vector2(iMid, j); //Debug.Log(wallPos); Vector2 oppoPos = GetOpposite(wallPos); Pair wallPair = !dir ? (new Pair(PointToParRay(stPos, wallPos + new Vector2(0, -0.5f), true), PointToParRay(stPos, wallPos + new Vector2(0, 0.5f), true))) : (new Pair(PointToParRay(stPos, wallPos + new Vector2(-0.5f, 0), true), PointToParRay(stPos, wallPos + new Vector2(0.5f, 0), true))); if (IsInRay(parRay, wallPair)) { if (copyWallGrid.ContainsKey(wallPos)) { Wall originWall = copyWallGrid[wallPos]; MapManager.inst.currentMap.CreateWall(oppoPos, originWall.type, false); SubtractRay(parRay, wallPair); } else { if (copyWallGrid.ContainsKey(oppoPos)) MapManager.inst.currentMap.RemoveWall(oppoPos); } } if (isSameRSide && Mathf.Abs(j) == (MapManager.inst.currentMap.maxMapSize + 1)) { if (anotherSide) { reflectSide *= -1; anotherSide = false; break; } else { anotherSide = true; reflectSide *= -1; j = mapRange - reflectSide; } } } // check floors for (int j = mapRange; Mathf.Abs(j) <= (MapManager.inst.currentMap.maxMapSize + 1); j += reflectSide) { Vector2 crossPoint = dir ? new Vector2(j + 0.5f * reflectSide, iMid) : new Vector2(iMid, j + 0.5f * reflectSide); if (IsInRayWeak(parRay, PointToParRay(stPos, crossPoint, true))) { Vector2Int[] floorPoses = { new Vector2Int(Mathf.FloorToInt(crossPoint.x), Mathf.FloorToInt(crossPoint.y)), new Vector2Int(Mathf.FloorToInt(crossPoint.x), Mathf.CeilToInt(crossPoint.y)), new Vector2Int(Mathf.CeilToInt(crossPoint.x), Mathf.FloorToInt(crossPoint.y)), new Vector2Int(Mathf.CeilToInt(crossPoint.x), Mathf.CeilToInt(crossPoint.y)) }; foreach (var floorPos in floorPoses) { int floorCount; if (floorCountGrid.TryGetValue(floorPos, out floorCount)) { if (floorCount == 0) floorCountGrid[floorPos]++; // have floor } else // no floor on there { floorCountGrid.Add(floorPos, -1); } } } if (isSameRSide && Mathf.Abs(j) == (MapManager.inst.currentMap.maxMapSize + 1)) { if (anotherSide) { reflectSide *= -1; anotherSide = false; break; } else { anotherSide = true; reflectSide *= -1; j = mapRange - reflectSide; } } } if (parRay.Count == 0) break; } while (!doReflect) yield return null; // copy floors foreach (var floorCount in floorCountGrid) { Vector2Int oppoPos = GetOpposite(floorCount.Key); if (floorCount.Value > 0) // copy origin floor to opposite { Floor originFloor = MapManager.inst.currentMap.GetFloorAtPos(floorCount.Key); Floor oppoFloor = MapManager.inst.currentMap.GetFloorAtPos(oppoPos); MapManager.inst.currentMap.CreateFloor(oppoPos, originFloor.isGoalFloor); if (oppoFloor != null) { if (oppoFloor.isPlayerOn) PlayerController.inst.RemovePlayer(oppoFloor); if (oppoFloor.objOnFloor != null) MapManager.inst.currentMap.RemoveObject(oppoPos); } if (originFloor.isPlayerOn) PlayerController.inst.CreatePlayer(oppoPos, floorCount.Key, dir); else if (originFloor.objOnFloor != null) { IObject obj = originFloor.objOnFloor; switch (obj.GetType()) { case ObjType.Mannequin: MapManager.inst.currentMap.CreateObject(oppoPos, ObjType.Mannequin, (obj as Mannequin).isWhite); GameObject tempMann = MapManager.inst.currentMap.GetObjectAtPos(floorCount.Key).GetObject(); GameObject oppoMann = MapManager.inst.currentMap.GetObjectAtPos(oppoPos).GetObject(); Quaternion mirroredRotation = tempMann.transform.rotation; Vector3 mirroredScale = tempMann.transform.localScale; mirroredRotation.w *= -1; if (dir) { mirroredRotation.z *= -1; mirroredScale.z *= -1; } else { mirroredRotation.x *= -1; mirroredScale.x *= -1; } oppoMann.transform.rotation = mirroredRotation; oppoMann.transform.localScale = mirroredScale; break; case ObjType.Briefcase: MapManager.inst.currentMap.CreateObject(oppoPos, ObjType.Briefcase, (obj as Briefcase).dropBullet); break; default: MapManager.inst.currentMap.CreateObject(oppoPos, obj.GetType()); break; } } } else if (floorCount.Value < 0) // remove opposite floor { Floor oppoFloor = MapManager.inst.currentMap.GetFloorAtPos(oppoPos); if (oppoFloor != null) { PlayerController.inst.RemovePlayer(oppoFloor); MapManager.inst.currentMap.RemoveObject(oppoPos); MapManager.inst.currentMap.RemoveFloor(oppoPos); } } } Break(); } /// /// subtract _sub from _parRay /// /// ray list to subtracted /// ray to subtract void SubtractRay(List _parRay, Pair _sub) { Pair toAdd = null; foreach (Pair pair in _parRay) { if (pair.r < _sub.l || pair.l > _sub.r) continue; List arr = new List() { pair.l, pair.r, _sub.l, _sub.r }; arr.Sort(); // subtract if (arr[0] == _sub.l && arr[2] == _sub.r) { pair.l = _sub.r; } else if (arr[1] == _sub.l && arr[3] == _sub.r) { pair.r = _sub.l; } else if (arr[1] == _sub.l && arr[2] == _sub.r) { toAdd = new Pair(_sub.r, pair.r); pair.r = _sub.l; } else if (arr[0] == _sub.l && arr[3] == _sub.r) { pair.r = pair.l; } } if (toAdd != null) _parRay.Add(toAdd); for (int i = 0; i < _parRay.Count; i++) { if (_parRay[i].r - _parRay[i].l < 0.001f) _parRay.Remove(_parRay[i]); } //Debug.Log("ray to subtract: " + _sub.l + "~" + _sub.r + "\nRay count: " + _parRay.Count); //foreach (var ray in _parRay) //{ // Debug.Log("Ray: " + ray.l + "~" + ray.r); //} } /// /// check if _range is included in _parRay /// /// ray list to be checked /// range to check /// if _range is included in _parRay, return true bool IsInRay(List _parRay, Pair _range) { bool output = false; foreach (Pair pair in _parRay) { //Debug.Log("IsinRay (" + pair.l + ", " + pair.r + ") " + _range.l + ", " + _range.r); if (pair.r <= _range.l || pair.l >= _range.r) continue; else { output = true; break; } } return output; } bool IsInRay(List _parRay, float _obj) { foreach (Pair pair in _parRay) { //Debug.Log("IsinRay (" + pair.l + ", " + pair.r + ") " + _obj); if (pair.l <= _obj && pair.r >= _obj) return true; } return false; } bool IsInRayWeak(List _parRay, Pair _range) { bool output = false; foreach (Pair pair in _parRay) { //Debug.Log("IsinRay (" + pair.l + ", " + pair.r + ") " + _range.l + ", " + _range.r); if (pair.r < _range.l || pair.l > _range.r) continue; else { output = true; break; } } return output; } bool IsInRayWeak(List _parRay, float _obj) { foreach (Pair pair in _parRay) { //Debug.Log("IsinRay (" + pair.l + ", " + pair.r + ") " + _obj); if (pair.l < _obj && pair.r > _obj) return true; } return false; } Vector2 GetOpposite(Vector2 objPos) { Vector2 output = new Vector2(objPos.x, objPos.y); if (dir) output.y = mapPos.y * 2 - objPos.y; else output.x = mapPos.x * 2 - objPos.x; return output; } Vector2Int GetOpposite(Vector2Int objPos) { Vector2 output = GetOpposite(new Vector2(objPos.x, objPos.y)); return new Vector2Int(Mathf.RoundToInt(output.x), Mathf.RoundToInt(output.y)); } /// /// calculate where _chPos is from _stPos /// /// position of shooter /// position of object /// if we calculate after reflecting, true /// float value of _chPos is posed float PointToParRay(Vector2 _stPos, Vector2 _chPos, bool _isRefl) { if (dir) { float px = (_chPos.x - _stPos.x) * (mapPos.y - _stPos.y) / (_isRefl ? 2 * mapPos.y - _chPos.y - _stPos.y : _chPos.y - _stPos.y) + _stPos.x; //Debug.Log("chPos: " + _chPos + ", output: " + (px - ldPos.x)); return px; } else { float py = (_chPos.y - _stPos.y) * (mapPos.x - _stPos.x) / (_isRefl ? 2 * mapPos.x - _chPos.x - _stPos.x : _chPos.x - _stPos.x) + _stPos.y; //Debug.Log("chPos: " + _chPos + ", output: " + (py - ldPos.y)); return py; } } }