def move(self): if self.rank_position is not None: self.rank_position.calculate_position() tx, ty = self.rank_position.x, self.rank_position.y if not utility.rough_match(self.x, tx, 0.5) or not utility.rough_match( self.y, ty, 0.5): if self.reforming: self.x = tx self.y = ty self.reforming = False else: dx, dy = tx - self.x, ty - self.y d = utility.distance((tx, ty), (self.x, self.y)) speed = self.unit.get_effective_speed() + 0.5 if d < speed: #If we're almost there, just jump exactly there self.x += dx self.y += dy else: self.x += dx / d * speed self.y += dy / d * speed if self.canvas: self.canvas.coords(self.id, self.x, self.y, self.x + TROOP_RADIUS, self.y + TROOP_RADIUS)
def move(self): if self.rank_position is not None: self.rank_position.calculate_position() tx, ty = self.rank_position.x, self.rank_position.y if not utility.rough_match(self.x, tx, 0.5) or not utility.rough_match(self.y, ty, 0.5): if self.reforming: self.x = tx self.y = ty self.reforming = False else: dx, dy = tx - self.x, ty - self.y d = utility.distance((tx, ty), (self.x, self.y)) speed = self.unit.get_effective_speed() + 0.5 if d < speed: #If we're almost there, just jump exactly there self.x += dx self.y += dy else: self.x += dx / d * speed self.y += dy / d * speed if self.canvas: self.canvas.coords(self.id, self.x, self.y, self.x + TROOP_RADIUS, self.y + TROOP_RADIUS)
def handle_diplomacy(self): # We can only start warring/trading if there are nations other than us. if len(self.parent.nations) > 1: distance_sorted_nations = sorted( self.parent.nations, key=lambda nation: utility.distance( self.get_average_city_position(), nation.get_average_city_position())) # Check for war/trade for other in distance_sorted_nations: # We can't start trading with an enemy we're at war with or vice versa. # And we certainly can't start fighting/trading with ourselves. if other != self and not other in self.at_war and not other in self.trading: normal_war_chance = int( max( 24, len(self.at_war) + 2 * self.get_tolerance() + 3 * self.relations[other.id])) holy_war_chance = int( max( 12, len(self.at_war) + self.get_tolerance() + 3 * self.relations[other.id])) effective_money = max( self.money**2, 1 ) # The less money we have, the more we want to trade. trade_chance = int( max( 24, math.log(effective_money) + self.get_tolerance() - 3 * self.relations[other.id])) # print('War chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], normal_war_chance)) # print('Holy war chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], holy_war_chance)) # print('Trade chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], trade_chance)) if random.randint(0, normal_war_chance) == 0: # print('Declaring war on nation with which out relations are {}'.format(self.relations[other.id])) self.parent.start_war(self, other) break # elif other.religion != self.religion and random.randint(0, holy_war_chance) == 0: # # print('Declaring a holy war on nation with which out relations are {}'.format(self.relations[other.id])) # self.parent.start_war(self, other, is_holy_war=True) # break elif random.randint(0, trade_chance) == 0: # print('Starting trade with a nation with which out relations are {}'.format(self.relations[other.id])) self.parent.start_trade_agreement(self, other) break self.remove_dead_nations() self.handle_army_dispatch() self.handle_revolt() self.handle_relations()
def in_range(self): if self.target is not None: tx, ty = self.target.x, self.target.y d = utility.distance((tx, ty), (self.x, self.y)) if self.ranged: return d <= self.get_ranged_weapon().range else: return d <= self.get_melee_weapon().range else: return False
def step(self, proj, best_material, best_enemy_material, stats, enemy_stats): if self.target is not None: #If we have a target, make sure it still exists. if not self.target in self.unit.target.soldiers: self.target = None if self.ranged and self.target is None: self.target = random.choice(self.unit.target.soldiers) else: self.target = utility.get_nearest_enemy(self, self.unit.target.soldiers) if self.target is not None: self.target.targeted = self else: return tx, ty = self.target.x, self.target.y d = utility.distance((self.x, self.y), (tx, ty)) if self.reload < self.reload_counter: if not self.ranged or self.in_range( ): # Melee units always reload, because their first attack is like a "charge" self.reload += self.discipline + random.randint(0, 1) #Show the weapon if we are currently in melee mode if self.canvas: if d != 0 and not self.ranged: cx, cy = self.x + TROOP_RADIUS // 2, self.y + TROOP_RADIUS // 2 weapon_range = self.get_melee_weapon().range if self.weapon_id == -1: self.weapon_id = self.canvas.create_line( cx, cy, cx, cy + weapon_range) else: self.canvas.coords(self.weapon_id, cx, cy, cx + (tx - cx) / d * weapon_range, cy + (ty - cy) / d * weapon_range) elif self.ranged: if self.weapon_id != -1: self.canvas.delete(self.weapon_id) self.weapon_id = -1 if self.ranged: self.handle_ranged(d, proj, best_material, stats, enemy_stats) else: self.handle_melee(d, best_material, best_enemy_material, stats, enemy_stats)
def get_movement_vector(self, vector_format='xy'): if self.target is not None and not self.soldier_type.ranged: if not self.in_range(): d = utility.distance((self.x, self.y), (self.target.x, self.target.y)) dx = self.target.x - self.x dy = self.target.y - self.y if vector_format == 'xy': return (float(dx) / d * self.get_effective_speed(), float(dy) / d * self.get_effective_speed()) elif vector_format == 'polar': # Magnitude and angle return (self.get_effective_speed(), math.atan2(dy, dx)) else: return (0, 0) else: return (0, 0)
def handle_ranged(self, d, proj, best_material, stats, enemy_stats): if d < self.target.unit.get_effective_speed() * CC_RANGE: self.ranged = False return self.move() if self.in_range() and self.reload >= self.reload_counter: if self.unit.ammunition > 0: weapon = self.get_ranged_weapon().name if not self.name in stats: stats[self.name] = utility.base_soldier_stats() if not weapon in stats[self.name]: stats[self.name][weapon] = utility.base_weapon_stats() stats['projectiles_launched'] += 1 stats[self.name]['projectiles_launched'] += 1 stats[self.name][weapon]['attacks'] += 1 m, tangle = self.unit.target.get_movement_vector(vector_format='polar') tx, ty = self.target.x, self.target.y if m > 0: t, angle = utility.calculate_interception(m, self.get_projectile_speed(), (tx, ty), (self.x, self.y), tangle) tx += math.cos(tangle) * t * m ty += math.sin(tangle) * t * m d = utility.distance((self.x, self.y), (tx, ty)) damage = self.get_ranged_attack(best_material) proj.append(Projectile((self.x, self.y), ((tx - self.x) / d, (ty - self.y) / d), self, damage, self.target, self.get_projectile_speed())) if self.canvas: color = self.canvas.itemcget(self.id, 'fill') proj[-1].id = self.canvas.create_oval(self.x, self.y, self.x + self.get_projectile_size(), self.y + self.get_projectile_size(), width=0, fill=color) proj[-1].skip_step = d // self.get_projectile_speed() // 2 proj[-1].kill_range = d // self.get_projectile_speed() * 2 self.reload = 0 self.unit.ammunition -= 1 else: for s in self.unit.soldiers: s.ranged = False self.unit.soldier_type.ranged = False
def move(self): if self.target is not None: tx, ty = self.target.get_position() self.dx, self.dy = tx - self.x, ty - self.y # print(self.x, self.y) # If we aren't in range, we need to move closer. if not self.in_range(): d = utility.distance((tx, ty), (self.x, self.y)) if d != 0: speed = self.get_effective_speed() if d < speed: self.x += self.dx self.y += self.dy else: self.x += self.dx / d * speed self.y += self.dy / d * speed
def step(self, proj, best_material, best_enemy_material, stats, enemy_stats): if self.target is not None: #If we have a target, make sure it still exists. if not self.target in self.unit.target.soldiers: self.target = None if self.ranged and self.target is None: self.target = random.choice(self.unit.target.soldiers) else: self.target = utility.get_nearest_enemy(self, self.unit.target.soldiers) if self.target is not None: self.target.targeted = self else: return tx, ty = self.target.x, self.target.y d = utility.distance((self.x, self.y), (tx, ty)) if self.reload < self.reload_counter: if not self.ranged or self.in_range(): # Melee units always reload, because their first attack is like a "charge" self.reload += self.discipline + random.randint(0,1) #Show the weapon if we are currently in melee mode if self.canvas: if d != 0 and not self.ranged: cx, cy = self.x + TROOP_RADIUS // 2, self.y + TROOP_RADIUS // 2 weapon_range = self.get_melee_weapon().range if self.weapon_id == -1: self.weapon_id = self.canvas.create_line(cx, cy, cx, cy + weapon_range) else: self.canvas.coords(self.weapon_id, cx, cy, cx + (tx - cx) / d * weapon_range, cy + (ty - cy) / d * weapon_range) elif self.ranged: if self.weapon_id != -1: self.canvas.delete(self.weapon_id) self.weapon_id = -1 if self.ranged: self.handle_ranged(d, proj, best_material, stats, enemy_stats) else: self.handle_melee(d, best_material, best_enemy_material, stats, enemy_stats)
def handle_diplomacy(self): # We can only start warring/trading if there are nations other than us. if len(self.parent.nations) > 1: distance_sorted_nations = sorted(self.parent.nations, key=lambda nation: utility.distance(self.get_average_city_position(), nation.get_average_city_position())) # Check for war/trade for other in distance_sorted_nations: # We can't start trading with an enemy we're at war with or vice versa. # And we certainly can't start fighting/trading with ourselves. if other != self and not other in self.at_war and not other in self.trading: normal_war_chance = int( max(24, len(self.at_war) + 2 * self.get_tolerance() + 3 * self.relations[other.id])) holy_war_chance = int( max(12, len(self.at_war) + self.get_tolerance() + 3 * self.relations[other.id])) effective_money = max(self.money ** 2, 1) # The less money we have, the more we want to trade. trade_chance = int( max(24, math.log(effective_money) + self.get_tolerance() - 3 * self.relations[other.id])) # print('War chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], normal_war_chance)) # print('Holy war chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], holy_war_chance)) # print('Trade chance with {} ({}) is {}'.format(other.name.short_name(), self.relations[other.id], trade_chance)) if random.randint(0, normal_war_chance) == 0: # print('Declaring war on nation with which out relations are {}'.format(self.relations[other.id])) self.parent.start_war(self, other) break # elif other.religion != self.religion and random.randint(0, holy_war_chance) == 0: # # print('Declaring a holy war on nation with which out relations are {}'.format(self.relations[other.id])) # self.parent.start_war(self, other, is_holy_war=True) # break elif random.randint(0, trade_chance) == 0: # print('Starting trade with a nation with which out relations are {}'.format(self.relations[other.id])) self.parent.start_trade_agreement(self, other) break self.remove_dead_nations() self.handle_army_dispatch() self.handle_revolt() self.handle_relations()
def handle_army_dispatch(self): # Determine if we want to launch an attack with this city's army for city in self.cities: if len(self.at_war) > 0 and city.army.size() > 0: enemy = random.choice(self.at_war) # Make sure our enemy actually still exists. if len(enemy.cities) > 0 and enemy in self.parent.nations: attacking_city = utility.weighted_random_choice( enemy.cities, weight=lambda _, v: 1.0 / utility.distance( city.position, v.position)) if random.randint( 0, max( 20, city.army.size() + city.population // 8 - attacking_city.population // 3 - attacking_city.army.size())) > 20: if random.randint(0, len(self.moving_armies)**3) == 0: fx, fy = city.position dx, dy = attacking_city.position # Conscript some levies to join the army. if city.population // 3 > 1: conscript_max = max(city.population // 4, 3) conscript_min = min(city.population // 8, 2) conscripted = int( random.randint(conscript_min, conscript_max) * self.get_conscription_bonus()) else: conscripted = 0 city.population -= conscripted city.army.add_to(city.army.name, conscripted) action = self.parent.do_attack( self, city, enemy, attacking_city) self.moving_armies.append( Group(self.parent, self.id, city.army, (fx, fy), (dx, dy), self.color, lambda s: False, action, is_army=True, has_boat=(city.resources['boats'] > 0))) self.parent.event_log.add_event( 'ArmyDispatched', { 'nation_a': self.id, 'nation_b': enemy.id, 'city_a': city.name, 'city_b': attacking_city.name, 'reason': 'attack', 'army_size': city.army.size() }, self.parent.get_current_date()) city.army = city.army.zero()
def handle_army_dispatch(self): # Determine if we want to launch an attack with this city's army for city in self.cities: if len(self.at_war) > 0 and city.army.size() > 0: enemy = random.choice(self.at_war) # Make sure our enemy actually still exists. if len(enemy.cities) > 0 and enemy in self.parent.nations: attacking_city = utility.weighted_random_choice(enemy.cities, weight=lambda _, v: 1.0 / utility.distance( city.position, v.position)) if random.randint(0, max(20, city.army.size() + city.population // 8 - attacking_city.population // 3 - attacking_city.army.size())) > 20: if random.randint(0, len(self.moving_armies) ** 3) == 0: fx, fy = city.position dx, dy = attacking_city.position # Conscript some levies to join the army. if city.population // 3 > 1: conscript_max = max(city.population // 4, 3) conscript_min = min(city.population // 8, 2) conscripted = int( random.randint(conscript_min, conscript_max) * self.get_conscription_bonus()) else: conscripted = 0 city.population -= conscripted city.army.add_to(city.army.name, conscripted) action = self.parent.do_attack(self, city, enemy, attacking_city) self.moving_armies.append( Group(self.parent, self.id, city.army, (fx, fy), (dx, dy), self.color, lambda s: False, action, is_army=True, has_boat=(city.resources['boats'] > 0))) self.parent.event_log.add_event('ArmyDispatched', {'nation_a': self.id, 'nation_b': enemy.id, 'city_a': city.name, 'city_b': attacking_city.name, 'reason': 'attack', 'army_size': city.army.size()}, self.parent.get_current_date()) city.army = city.army.zero()
def handle_ranged(self, d, proj, best_material, stats, enemy_stats): if d < self.target.unit.get_effective_speed() * CC_RANGE: self.ranged = False return self.move() if self.in_range() and self.reload >= self.reload_counter: if self.unit.ammunition > 0: weapon = self.get_ranged_weapon().name if not self.name in stats: stats[self.name] = utility.base_soldier_stats() if not weapon in stats[self.name]: stats[self.name][weapon] = utility.base_weapon_stats() stats['projectiles_launched'] += 1 stats[self.name]['projectiles_launched'] += 1 stats[self.name][weapon]['attacks'] += 1 m, tangle = self.unit.target.get_movement_vector( vector_format='polar') tx, ty = self.target.x, self.target.y if m > 0: t, angle = utility.calculate_interception( m, self.get_projectile_speed(), (tx, ty), (self.x, self.y), tangle) tx += math.cos(tangle) * t * m ty += math.sin(tangle) * t * m d = utility.distance((self.x, self.y), (tx, ty)) damage = self.get_ranged_attack(best_material) proj.append( Projectile((self.x, self.y), ((tx - self.x) / d, (ty - self.y) / d), self, damage, self.target, self.get_projectile_speed())) if self.canvas: color = self.canvas.itemcget(self.id, 'fill') proj[-1].id = self.canvas.create_oval( self.x, self.y, self.x + self.get_projectile_size(), self.y + self.get_projectile_size(), width=0, fill=color) proj[-1].skip_step = d // self.get_projectile_speed() // 2 proj[-1].kill_range = d // self.get_projectile_speed() * 2 self.reload = 0 self.unit.ammunition -= 1 else: for s in self.unit.soldiers: s.ranged = False self.unit.soldier_type.ranged = False