def unit_movement(battle: Battle): # first pass: desire positions / optimistic move for battle_unit_in_turn in BattleUnitInTurn.objects.filter( battle_turn=battle.get_latest_turn()): for battle_contubernium_in_turn in battle_unit_in_turn.battlecontuberniuminturn_set.all( ): target_distance_function = get_target_distance_function( battle_contubernium_in_turn) if target_distance_function: optimistic_move_desire_formulation(battle_contubernium_in_turn, target_distance_function) optimistic_move_desire_resolving(battle) # second pass: if could not move, do "safe" move for battle_unit_in_turn in BattleUnitInTurn.objects.filter( battle_turn=battle.get_latest_turn()): for battle_contubernium_in_turn in battle_unit_in_turn.battlecontuberniuminturn_set.filter( moved_this_turn=False): target_distance_function = get_target_distance_function( battle_contubernium_in_turn) if target_distance_function: safe_move(battle_contubernium_in_turn, target_distance_function) # finalize for battle_unit_in_turn in BattleUnitInTurn.objects.filter( battle_turn=battle.get_latest_turn()): battle_unit_in_turn.update_pos() check_if_order_done(battle_unit_in_turn)
def optimistic_move_desire_resolving(battle: Battle): while BattleContuberniumInTurn.objects.filter( desires_pos=True, battle_turn=battle.get_latest_turn()).exists(): bcuit = BattleContuberniumInTurn.objects.filter( desires_pos=True, battle_turn=battle.get_latest_turn())[0] contubernia_desiring_position = battle.get_latest_turn( ).get_contubernia_desiring_position(bcuit.desired_coordinates()) desired_position_occupier = battle.get_latest_turn( ).get_contubernium_in_position(bcuit.desired_coordinates()) if desired_position_occupier: # test if mutually desiring positions if desired_position_occupier.desires_pos: for desirer in contubernia_desiring_position: if desirer.coordinates( ) == desired_position_occupier.desired_coordinates(): grant_position_swap(desirer, desired_position_occupier) continue # TODO: try to move blocking contubernium before giving up contubernia_desiring_position.update(desires_pos=False) else: contubernia_desiring_position.update(desires_pos=False) else: desire_getter = get_highest_priority_desire( contubernia_desiring_position) grant_position_desire(desire_getter) contubernia_desiring_position.update(desires_pos=False)
def unit_attack(battle: Battle): contubernia = list( BattleContuberniumInTurn.objects.filter( battle_turn=battle.get_latest_turn())) random.shuffle(contubernia) for contubernium in contubernia: world_unit = contubernium.battle_unit_in_turn.battle_unit.world_unit enemy_contubernia = BattleContuberniumInTurn.objects.filter( battle_turn=contubernium.battle_turn).exclude( battle_contubernium__battle_unit__battle_side=contubernium. battle_contubernium.battle_unit.battle_side) target_contubernium, distance = \ closest_in_set(contubernium.coordinates(), enemy_contubernia) if target_contubernium is None: continue if distance < 2: unit_attack_melee(contubernium, target_contubernium) contubernium.attack_type_this_turn = \ BattleContuberniumInTurn.MELEE_ATTACK contubernium.contubernium_attacked_this_turn = \ target_contubernium contubernium.save() elif (world_unit.is_ranged() and distance <= world_unit.shot_range() and contubernium.ammo_remaining > 0 and not contubernium.moved_this_turn): unit_attack_ranged(contubernium, target_contubernium) contubernium.attack_type_this_turn = \ BattleContuberniumInTurn.RANGED_ATTACK contubernium.contubernium_attacked_this_turn = \ target_contubernium contubernium.save()
def battle_tick(battle: Battle): with perf_timer("Tick {} for {}".format(battle.get_latest_turn().num, battle)): create_next_turn(battle) unit_movement(battle) unit_attack(battle) if check_end(battle): battle.current = False battle.save()
def check_end(battle: Battle): for side in battle.battleside_set.all(): if not BattleSoldierInTurn.objects.filter( battle_turn=battle.get_latest_turn(), battle_contubernium_in_turn__battle_contubernium__battle_unit__battle_side =side, wound_status__lt=BattleSoldierInTurn.DEAD).exists(): return True side_0_contubs = BattleContuberniumInTurn.objects.filter( battle_turn=battle.get_latest_turn(), battle_contubernium__battle_unit__battle_side__z=False) side_1_contubs = BattleContuberniumInTurn.objects.filter( battle_turn=battle.get_latest_turn(), battle_contubernium__battle_unit__battle_side__z=False) for contub in side_0_contubs: closest, distance = closest_in_set(contub.coordinates(), side_1_contubs) if distance is not None and distance < 40: return False return True
def unit_attack(battle: Battle): contubernia = list( BattleContuberniumInTurn.objects.filter( battle_turn=battle.get_latest_turn())) random.shuffle(contubernia) for contubernium in contubernia: enemy_contubernia = BattleContuberniumInTurn.objects.filter( battle_turn=contubernium.battle_turn).exclude( battle_contubernium__battle_unit__battle_side=contubernium. battle_contubernium.battle_unit.battle_side) target_contubernium, distance = \ closest_in_set(contubernium.coordinates(), enemy_contubernia) if target_contubernium is not None and distance < 2: for soldier in contubernium.battlesoldierinturn_set.all(): target_soldier = random.choice( target_contubernium.battlesoldierinturn_set.all()[:]) while (random.random() < 0.5 * soldier.attack_chance_multiplier()): if target_soldier.wound_status < BattleSoldierInTurn.DEAD: target_soldier.take_hit() target_soldier.save()
def create_next_turn(battle: Battle): new_turn = battle.get_latest_turn() new_turn.id = None new_turn.num += 1 new_turn.save() prev_turn = battle.battleturn_set.get(num=new_turn.num - 1) for side in battle.battleside_set.all(): for organization in side.battleorganization_set.all(): for character in organization.battlecharacter_set.all(): bcit = BattleCharacterInTurn.objects.get( battle_character=character, battle_turn=prev_turn) bcit.id = None bcit.battle_turn = new_turn bcit.save() for unit in organization.battleunit_set.all(): try: buit = BattleUnitInTurn.objects.get(battle_unit=unit, battle_turn=prev_turn) if not buit.battlecontuberniuminturn_set.filter( x_pos__gte=-50, x_pos__lte=50, z_pos__gte=-50, z_pos__lte=50, ).exists(): buit.battle_unit.in_battle = False buit.battle_unit.save() buit.battle_unit.world_unit.status = \ WorldUnit.REGROUPING buit.battle_unit.world_unit.save() continue if not BattleSoldierInTurn.objects.filter( battle_contubernium_in_turn__battle_unit_in_turn= buit, wound_status__lt=BattleSoldierInTurn.DEAD).exists( ): buit.battle_unit.in_battle = False buit.battle_unit.save() buit.battle_unit.world_unit.status = \ WorldUnit.REGROUPING buit.battle_unit.world_unit.save() continue buit.id = None buit.battle_turn = new_turn buit.battle_character_in_turn = BattleCharacterInTurn.objects.get( battle_turn=new_turn, battle_character=buit.battle_character_in_turn. battle_character ) if buit.battle_character_in_turn is not None else None buit.order = buit.battle_unit.get_order() buit.save() except BattleUnitInTurn.DoesNotExist: continue for contubernium in unit.battlecontubernium_set.all(): try: bcontubit = BattleContuberniumInTurn.objects.get( battle_contubernium=contubernium, battle_turn=prev_turn) if not bcontubit.battlesoldierinturn_set.filter( wound_status__lt=BattleSoldierInTurn.DEAD ).exists(): continue if (not -50 <= bcontubit.x_pos <= 50 or not -50 <= bcontubit.z_pos <= 50): continue bcontubit.id = None bcontubit.moved_this_turn = False bcontubit.desires_pos = False bcontubit.battle_turn = new_turn bcontubit.battle_unit_in_turn = buit bcontubit.save() except BattleContuberniumInTurn.DoesNotExist: pass for soldier in contubernium.battlesoldier_set.all(): try: bsit = BattleSoldierInTurn.objects.get( battle_turn=prev_turn, battle_soldier=soldier) if bsit.wound_status == BattleSoldierInTurn.DEAD: continue bsit.id = None bsit.battle_turn = new_turn bsit.battle_contubernium_in_turn = bcontubit bsit.save() except BattleSoldierInTurn.DoesNotExist: pass