Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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()
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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)
Example #7
0
    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)
Example #8
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
Example #9
0
    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)
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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)
Example #13
0
    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()
Example #14
0
    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()
Example #15
0
    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()
Example #16
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