def Draw(self, canvas, viewport): # Draw each city battlefield cell if (Battles.Utils.Settings.SETTINGS.Get_B('City', 'DisplayOldTownGrid')): for c in self.__battleFieldCells: bbox = c.GetBoundingQuad() vp1 = viewport.W2V(bbox.minPoint) vp2 = viewport.W2V(bbox.maxPoint) canvas.create_rectangle(vp1.x, vp1.y, vp2.x, vp2.y, fill="light yellow") # Draw the buildings housesize = Battles.Utils.Settings.SETTINGS.Get_F( 'City', 'Houses', 'Size') for p in self.__buildings: vp1 = viewport.W2V(Point2D(p[0] - housesize, p[1] - housesize)) vp2 = viewport.W2V(Point2D(p[0] + housesize, p[1] + housesize)) canvas.create_rectangle(vp1.x, vp1.y, vp2.x, vp2.y, fill="Ghost White")
def DrawHeightView(self, enemyarmy, canvas, viewport): # Display the wall in a height view if len(self.__heightCanvasObjs) > 0: for c in self.__heightCanvasObjs: canvas.delete(c) # Draw the tiles self.__heightCanvasObjs.extend(self.__tilesManager.DrawTiles(canvas, viewport)) l = self.GetLength() # Draw the wall bounding p1 = Point2D(0, 0) p2 = Point2D(l, self._height) pp1 = viewport.W2V(p1) pp2 = viewport.W2V(p2) self.__heightCanvasObjs.append(canvas.create_rectangle(pp1.x, pp1.y, pp2.x, pp2.y, fill=None, outline="blue")) # Draw the rubble (the order here is important to avoid overlapping) self.__heightCanvasObjs.extend(self.__tilesManager.DrawRubble(canvas, viewport)) # Draw the wall climbers for stair in self.__climberStairs: self.__heightCanvasObjs.extend(stair.Draw(canvas, viewport))
def GetBoundingQuad(self): # Return the bounding rectangle in world coordinates center = self.GetCenterPosition() minP = Point2D(center.x - (Battalion.bounding.length / 2.0), center.y - (Battalion.bounding.width / 2.0)) maxP = Point2D(center.x + (Battalion.bounding.length / 2.0), center.y + (Battalion.bounding.width / 2.0)) # Note that this is true since the battlefield is orthonormal return BoundingQuad(minPoint = minP, maxPoint = maxP)
def GetBoundingQuad(self): p1 = Point2D() p2 = Point2D() cellsize = self.__battleField.GetCellSize() p1.x = self.__index["column"] * cellsize p1.y = self.__index["row"] * cellsize p2.x = p1.x + cellsize p2.y = p1.y + cellsize bq = BoundingQuad() bq.InsertPoint(p1) bq.InsertPoint(p2) return bq
def CastleData(self, castle): # A simple and miniature four-walls castle polyline = [ Point2D(18.5, 60.0), Point2D(75.0, 70.0), Point2D(75.0, 90.0), Point2D(25.0, 90.0) ] #polyline = [Point2D(25.0, 60.0), Point2D(75.0, 70.0), Point2D(90.0, 90.0), Point2D(35.0, 80.0)] castle.ConstructCurtainWall(polyline) castle.SetCastleOrientation(Vector2D(0.0, -1.0)) castle.SetWallsResistance(100000) castle.Evolve(climbings=None, attachedsiegetowers=None, battlefield=None)
def DrawHeightView(self, canvas, viewport, position): # Draw the battalion in height view. Draw an empty rectangle, and fills it with the remaining reload time (max reload time means empty rectangle) # Given position is the center of battalion # Returns the canvas object ret = [] # Draw the empty battalion bounding rectangle p1 = Point2D(position.x - (self.GetBounding().length / 2.0), position.y) p2 = Point2D(position.x + (self.GetBounding().length / 2.0), position.y - self.GetBounding().height) pp1 = viewport.W2V(p1) pp2 = viewport.W2V(p2) if (self._action.IsReloadReady()): # The battalion is going to attack, so it must be shown as a filed rectangle at 100% ret.append( canvas.create_rectangle(pp1.x, pp1.y, pp2.x, pp2.y, fill="DarkBlue", outline="gray")) else: ret.append( canvas.create_rectangle(pp1.x, pp1.y, pp2.x, pp2.y, fill=None, outline="gray")) # Draw the filled rectangle from the reload remaining time remain = self._action.GetReloadRemainingTime(percentage=True, nextt=False) p2 = Point2D(position.x + (self.GetBounding().length / 2.0), (position.y - self.GetBounding().height) * (remain)) pp2 = viewport.W2V(p2) ret.append( canvas.create_rectangle(pp1.x, pp1.y, pp2.x, pp2.y, fill="DarkBlue", outline="gray")) return ret
def CastleData(self, castle): # Define the city city = [[200.0, 230.0], [230.0, 231.0], [200.0, 292.0], [230.0, 233.0], [230.0, 234.0], [243.0, 234.0], [330.0, 250.0], [245.0, 333.0], [300.0, 304.0], [238.0, 230.0], [243.0, 260.0]] castle.WrapOldCity(city=city, margin=60, battlefieldcenter=Point2D(250.0, 250.0)) castle.SetWallsResistance(100000) castle.SetCastleOrientation(Vector2D(0.0, -1.0)) castle.Evolve(climbings=None, attachedsiegetowers=None, battlefield=None) """data = StarFortressData() data.RavelinRadius = 25.0 data.RavelinMinWidth = 15.0 data.HasHalfMoons = True data.HalfMoonCircleOffset = 5.0 data.HalfMoonLength = 30.0 data.CovertWayThickness = 10.0 data.CovertWayOffset = 5.0 data.CovertWayHasPlacesOfArms = True data.CovertWayPlacesOfArmsLength = 10.0 data.GlacisThickness = 15.0 castle.ConstructStarFortress(data) """ # Sets the moat object. Note that the moat geometry isn't yet calcualted until the castle is deployed in the battlefield castle.SetMoat(thickness=Battles.Utils.Settings.SETTINGS.Get_F( 'Castle', 'Moat', 'Depth') * 1, depth=Battles.Utils.Settings.SETTINGS.Get_F( 'Castle', 'Moat', 'Depth'), haswater=False)
def DrawCastleShape(self, canvas=None, city=False, starfortress=False, resetData=True): # Draws the castle and city shape. If canvas is none, creates a new tkinter and canvas and executes a mainloop to show the window permanently # Setup window data if not canvas: loop = True tkr = Tk() canvas = Canvas(tkr, height=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowHeight'), width=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowWidth'), bg="white") canvas.pack() else: loop = False # For the first evolution, we must draw the initial castle shape. We cannot do it here because the castle has not been yet initialized. # But we cannot do it later, because the window is invalidated only when main thread is released waiting the "after" calling (see below) # For that reason we create here the castle and battlefield data self.__data.CastleData(self.__castle) self.__isCastleConstructed = True self.__battlefield = self.__data.BattleFieldData() viewport = Viewport(Bounding(Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowWidth'), Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowHeight')), self.__battlefield.GetBounding(), Point2D(x=0.0, y=0.0)) self.__castle.Draw(canvas, viewport, moat=False, city=city, starfortress=starfortress) if resetData: self._resetData(removeCastle=False, removeBattleField=False, resetCounters=False) if loop: mainloop()
def __init__(self): self.center = Point2D() self.leftFlank = Segment2D() self.rightFlank = Segment2D() self.leftRear = Segment2D() self.rightRear = Segment2D() self.radius = 0.0
def DistanceFromPoint(self, posfrom, squared=False): # Calculate the distance from given point to wall # Work in 2D to simplify p = Point2D(posfrom.x, posfrom.y) seg = Segment2D(self.GetStartPosition(), self.GetEndPosition()) return seg.DistanceToPoint(p, squared)
def AppendBattalion(self, battalion, offset=0): # Appends a battalion that requires size space at next avaiable position # Returns false if there isn't enough space to place the battalion # Parameter offset is used to place the next battalion at offset distance # Calculate next avaiable placement if (len(self._battalions) == 0): pend = self._line.p1.Copy() else: pend = Point2D().SetFrom3D(self._battalions[len(self._battalions) - 1].position.Copy()) dv = self.__GetVector2D() dv.Normalize() pend.Move(dv, (self._gridCellSize) + offset) # Check if new placement has reached the end of defending line dist1 = pend.Distance(self._line.p1) dist2 = pend.Distance(self._line.p2) endreached = (dist2 < self._gridCellSize) or (dist1 > self.__GetLength()) if (endreached): if (len(self._battalions) > 0): return False else: # If there are not enough space to deploy the battalion, but the defense line is empty, means that it is too short, but it is enough large to fit an unit # This can happens if offset is too big if (len(self._battalions) == 0): pend = self._line.GetMidPoint() self._battalions.append( DefendingBattalion(battalion, Point3D(pend.x, pend.y, self._height))) self._battalionsMap[battalion.GetLabel()] = len(self._battalions) - 1 return True
def canAppendBattalion(self, offset=0): # verifies if a battalion that requires size space at next available position can be appended # Returns false if there isn't enough space to place the battalion # Returns true otherwise # Calculate next available placement if (len(self._battalions) == 0): pend = self._line.p1.Copy() else: pend = Point2D().SetFrom3D(self._battalions[len(self._battalions) - 1].position.Copy()) dv = self.__GetVector2D() dv.Normalize() pend.Move(dv, (self._gridCellSize) + offset) # Check if new placement has reached the end of defending line dist1 = pend.Distance(self._line.p1) dist2 = pend.Distance(self._line.p2) endreached = (dist2 < self._gridCellSize) or (dist1 > self.__GetLength()) if (endreached): if (len(self._battalions) > 0): return False return True
def PlayCastleEvolution(self): # random.seed() self.__evolve_result["Evolutions"] = 0 self.__evolve_result["Invictus"] = False self.__evolve_result["EternalDefeat"] = False # Setup window data self.__evolve_tkinter = Tk() self.__evolve_canvas = Canvas(self.__evolve_tkinter, height=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowHeight'), width=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowWidth'), bg="white") self.__evolve_canvas.pack() Battles.Utils.Message.Verbose = Battles.Utils.Message.VERBOSE_STATISTICS # Battles.Utils.Message.Log('The Battles for honor and glory have begun!!!! (castle evolution mode)', # Battles.Utils.Message.VERBOSE_EXTRA) # For the first evolution, we must draw the initial castle shape. We cannot do it here because the castle has not been yet initialized. # But we cannot do it later, because the window is invalidated only when main thread is released waiting the "after" calling (see below) # For that reason we create here the castle and battlefield data self.__data.CastleData(self.__castle) self.__isCastleConstructed = True self.__battlefield = self.__data.BattleFieldData() self.__evolve_viewport = Viewport(Bounding(Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowWidth'), Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowHeight')), self.__battlefield.GetBounding(), Point2D(x=0.0, y=0.0)) self.__castle.Draw(self.__evolve_canvas, self.__evolve_viewport, moat=False, city=False, starfortress=False) self._resetData(removeCastle=False, removeBattleField=False, resetCounters=False) self.__evolve_tkinter.after(1000, self.__PlayCastleEvolution_Step) mainloop()
def CropInside(self, convexhull): # Crops the moat cells that are inside given convex hull clist = [] """ if (convexhull): for c in self._battleFieldCells: center = Point2D().SetFrom3D(c.center) if (not convexhull.IsInside(center)): clist.append(c) else: c.RemoveMoat() """ # IsInside method is not accurate for this purpose # Use the GPC # TODO: Create a GPC instance to avoid passing the points list each time gpc = GPCWrapper() plist = convexhull.GetPointsList() for c in self._battleFieldCells: center = Point2D().SetFrom3D(c.center) if (not gpc.IsInside(plist, center)): clist.append(c) else: c.RemoveMoat() self._battleFieldCells = clist
def __init__(self): self.__battalions = [] self.__defendersArmy = None self.__position = Point2D() self.__throwers = None self.__construction = None self.__waitingBattalion = None
def GetTile(self, pos): # Return the tile that matches with given 3D position startpos = self.__construction.GetStartPosition() #dist = startpos.Distance(pos) xdist = Point2D().SetFrom3D(startpos).Distance( Point2D().SetFrom3D(pos)) row = int(math.floor(pos.z / self.__tileDims["height"])) if (row >= self.__tilesSize["rows"]): row = int(self.__tilesSize["rows"] - 1) column = int(math.floor(xdist / self.__tileDims["width"])) if (column >= self.__tilesSize["columns"]): column = int(self.__tilesSize["columns"] - 1) return self.__tiles[row][column]
def ProjectPosition(self, pos): # Projects given position onto the defending line to get a centered good position pos2d = Point2D().SetFrom3D(pos) prj = self._line.ProjectPoint(pos2d) prj3d = Point3D().SetFrom2D(prj) prj3d.z = pos.z return prj3d
def DeployAttackersData(self, attackers, battlefield, castle): battlefield.DeployBattalion(position=Point2D(00.0, 20.0), lines=1, maxpercell=-1, army=attackers, kind="Infantry", number=-1) battlefield.DeployBattalion(position=Point2D(10.0, 10.0), lines=1, maxpercell=5, army=attackers, kind="Archers", number=-1) battlefield.DeployBattalion(position=Point2D(30.0, 0.0), lines=1, maxpercell=1, army=attackers, kind="Cannons", number=-1)
def isInsideCastle(point): from Battles.Utils.Geometry import Point2D global castle pW = gridToWorld(point) for c in castle.GetCastlesList(): # cannot make this more inefficient... I don't care! jp = c.GetJoinsPolygon() if jp.IsInside(Point2D(pW[0], pW[1])): # print "inside castle!!!" return True return False
def gridToWorld(gridCoords): from Battles.Utils.Geometry import Point2D # wCoords = game.GetBattlefield().GetCellSize() * gridCoords c = battlefield.GetCell(int(gridCoords[0]), int(gridCoords[1])) if c: wCoords = c.center else: wCoords = Point2D(0, 0) print "conversion GridToW Failed:", gridCoords[0], gridCoords[1] # print "conversion GridToW:", gridCoords[0], gridCoords[1], '->', wCoords.x, wCoords.y return np.array([wCoords.x, wCoords.y])
def GetRubbleHeight(self, position): # Returns the rubble height (if any) on given 3D position # Get the tile column to know the rubble slot p = Point2D().SetFrom3D(position) distx = p.Distance(self.__construction.GetStartPosition()) col = int(math.floor(distx / self.__tileDims["width"])) if (col >= len(self.__rubble)): col = len(self.__rubble) - 1 # This should never happen return self.__rubble[col]
def CircleIntersects(self, center, radius): # Return a list with all cells that intersect with given circle. Only are checked the cell vertices and cell center bbox = self.GetBoundingQuad() if (bbox.minPoint.IsInCircle(center, radius)): return True if (bbox.maxPoint.IsInCircle(center, radius)): return True p = Point2D(bbox.minPoint.x, bbox.maxPoint.y) if (p.IsInCircle(center, radius)): return True p = Point2D(bbox.maxPoint.x, bbox.minPoint.y) if (p.IsInCircle(center, radius)): return True p = Point2D().SetFrom3D(self.center) if (p.IsInCircle(center, radius)): return True return False
def DrawRubble(self, canvas, viewport): # Draw the rubble height lines ret = [] wlength = self.__construction.GetLength() wheight = self.__construction.GetHeight() j = 0 while (j < self.__tilesSize["columns"]): if ((j + 1) == self.__tilesSize["columns"]): offsetX = wlength - (j * self.__tileDims["width"]) else: offsetX = self.__tileDims["width"] rh = wheight - self.__rubble[j] rp1 = Point2D(j * self.__tileDims["width"], rh) rp2 = Point2D(rp1.x + offsetX, rh) rrp1 = viewport.W2V(rp1) rrp2 = viewport.W2V(rp2) if (j in self.__gateways): color = "green" else: color = "Khaki" ret.append( canvas.create_line(rrp1.x, rrp1.y, rrp2.x, rrp2.y, fill=color, width=4)) j += 1 return ret
def Draw(self, canvas, viewport): # Draw the stair and their battalions in height view. Also, draw the thrower battalion (if it is defined) in height view. Return a list with all canvas objects ret = [] h = self.GetHeight() dist = self.__construction.GetStartPosition().Distance( Point2D().SetFrom3D(self.__position)) for e in self.__battalions: s = e.GetCenterPosition() p1 = Point2D(dist, h - s.z) p2 = Point2D(dist + e.GetBounding().length, h - (s.z + e.GetBounding().height)) pp1 = viewport.W2V(p1) pp2 = viewport.W2V(p2) ret.append( canvas.create_rectangle(pp1.x, pp1.y, pp2.x, pp2.y, fill="red", outline="gray")) if (self.__throwers): canvobjs = self.__throwers.DrawHeightView(canvas, viewport, position=Point2D( dist, 0.0)) if (canvobjs): ret.extend(canvobjs) return ret
def __init__(self, battlefield, castle, defenders, attackers, showBoard=True, verbose=True): self.__battleField = battlefield self.__castle = castle self.__defenders = defenders self.__attackers = attackers self.__speed = Battles.Utils.Settings.SETTINGS.Get_I('Game', 'speed') self.__showBoard = showBoard self.__bypassedViewData = False self.__endControl = None if self.__showBoard: self.__masterTK = Tk() self.__canvas = Canvas(self.__masterTK, height=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowHeight'), width=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'WindowWidth'), bg="white") self.__canvas.pack() self.__viewport = Viewport(Bounding(Battles.Utils.Settings.SETTINGS.Get_I('Game', 'ViewportWidth'), Battles.Utils.Settings.SETTINGS.Get_I('Game', 'ViewportHeight')), self.__battleField.GetBounding(), Point2D(x=0.0, y=0.0)) self.__battleField.Draw(self.__canvas, self.__viewport, showgrid=Battles.Utils.Settings.SETTINGS.Get_B(category='Game', tag='ShowGrid')) self.__castle.Draw(self.__canvas, self.__viewport, moat=True, city=True, starfortress=False) self.__attackers.Draw(self.__canvas, self.__viewport) self.__defenders.Draw(self.__canvas, self.__viewport) self.__shoots = [] self.__heightViews = [] self.__canvas.bind("<1>", lambda event: self.__canvas.focus_set()) self.__canvas.bind("<Key>", self.KeyPressed) else: self.__masterTK = None self.__canvas = None self.__viewport = None self.__shoots = None self.__heightViews = None self.__stepStatus = self.__STEP_INIT self.__roundsCounter = 0 self.__resultStatistics = None
def printASCIICastle(steps=10.): from Battles.Utils.Geometry import Point2D for c in castle.GetCastlesList(): jp = c.GetJoinsPolygon() bb = c.GetBounding() for y in np.linspace(bb.GetCenter().y - bb.GetWidth(), bb.GetCenter().y + bb.GetWidth(), bb.GetWidth() / steps): print "#", for x in np.linspace(bb.GetCenter().x - bb.GetLength(), bb.GetCenter().x + bb.GetLength(), bb.GetLength() / steps): if jp.IsInside(Point2D(x, y)): # game.GetCastle().isInside(Point2D(x,y)): print '.', else: print ' ', print "#"
def CastleData(self, castle): # Define the city city = [[200.0, 230.0], [230.0, 231.0], [200.0, 292.0], [230.0, 233.0], [230.0, 234.0], [243.0, 234.0], [330.0, 250.0], [245.0, 333.0], [300.0, 304.0], [238.0, 230.0], [243.0, 260.0]] castle.WrapOldCity(city=city, margin=20, battlefieldcenter=Point2D(250.0, 250.0)) castle.SetWallsResistance(100000) castle.SetCastleOrientation(Vector2D(0.0, -1.0)) castle.Evolve(climbings=None, attachedsiegetowers=None, battlefield=None) # Sets the moat object. Note that the moat geometry isn't yet calcualted until the castle is deployed in the battlefield castle.SetMoat(thickness=Battles.Utils.Settings.SETTINGS.Get_F( 'Castle', 'Moat', 'Depth') * 1, depth=Battles.Utils.Settings.SETTINGS.Get_F( 'Castle', 'Moat', 'Depth'), haswater=False)
def Draw(self, canvas, viewport): vp1 = viewport.W2V(self.leftFlank.p1) vp2 = viewport.W2V(self.leftFlank.p2) vp3 = viewport.W2V(self.rightFlank.p2) vp4 = viewport.W2V(self.rightRear.p2) vp5 = viewport.W2V(self.leftRear.p1) canvas.create_line(vp1.x, vp1.y, vp2.x, vp2.y, fill="brown", width="2") canvas.create_line(vp2.x, vp2.y, vp3.x, vp3.y, fill="brown", width="2") canvas.create_line(vp3.x, vp3.y, vp4.x, vp4.y, fill="brown", width="2") canvas.create_line(vp5.x, vp5.y, vp1.x, vp1.y, fill="brown", width="2") v1 = Vector2D().CreateFrom2Points(self.center, self.rightRear.p2) v2 = Vector2D().CreateFrom2Points(self.center, self.leftRear.p1) ang1 = v1.GetAngle() if (ang1 > 360.0): ang1 -= 360.0 if (ang1 < 0.0): ang1 += 360.0 ang2 = v2.GetAngle() if (ang2 > 360.0): ang2 -= 360.0 if (ang2 < 0.0): ang2 += 360.0 angbetween = v1.AngleBetween(v2) lastp = viewport.W2V(self.leftRear.p1) i = 0 while (i < angbetween): p = Point2D() p.x = self.center.x + (self.radius * math.cos(math.radians(ang2))) p.y = self.center.y + (self.radius * math.sin(math.radians(ang2))) vp = viewport.W2V(p) canvas.create_line(lastp.x, lastp.y, vp.x, vp.y, fill="brown", width="2") # if (vp.Distance(lastp) > 1): # a = 1 lastp = vp ang2 += 1.0 i += 1.0
def __init__(self, construction, enemyarmy): self.__construction = construction self.__view = Toplevel() self.__canvas = Canvas(self.__view, height=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'HeightViewHeight'), width=Battles.Utils.Settings.SETTINGS.Get_I('Game', 'HeightViewWidth'), bg="white") self.__canvas.pack() self.__view.title(construction.GetLabel()) self.__enemyarmy = enemyarmy # Creates the viewport cb = self.__construction.GetBoundingHeightView() self.__viewport = Viewport(viewSize=Bounding(Battles.Utils.Settings.SETTINGS.Get_I('Game', 'HeightViewWidth'), Battles.Utils.Settings.SETTINGS.Get_I('Game', 'HeightViewHeight')), worldSize=cb) # Center view (approx) center = Point2D(cb.length * 0.05, cb.width * 0.1) self.__viewport.SetOffset(center) self.__construction.DrawHeightView(self.__enemyarmy, self.__canvas, self.__viewport)
def Wrap(self, city, margin, battlefieldcenter): # From a list of 2D points, constructs the convex hull around the castle with specified margin # battlefieldcenter is a Point2D with the center of the battlefield. To avoid biased results due some castle walls are closest to the attackers than others, c # enter here the city in the battle field # Calculate the bounding box and the displacement of it from the battlefield center. Then, apply this displacement to all city coordinates box = BoundingQuad() for c in city: box.InsertPoint(Point2D(c[0], c[1])) center = box.GetCenter() distx = battlefieldcenter.x - center.x disty = battlefieldcenter.y - center.y for c in city: c[0] += distx c[1] += disty self.__buildings = city self.__convexhull = ConvexHull(city) self.__convexhull.Calculate() self.__convexhull.SetMargin(margin)