diff --git a/objects/obj_controller/Create_0.gml b/objects/obj_controller/Create_0.gml index 64e4dd957b..1de2c0e1e1 100644 --- a/objects/obj_controller/Create_0.gml +++ b/objects/obj_controller/Create_0.gml @@ -808,18 +808,18 @@ bat_formation = array_create(_count, ""); bat_formation_type = array_create(_count, 0); bat_deva_for = array_create(_count, 1); bat_assa_for = array_create(_count, 4); -bat_tact_for = array_create(_count, 2); +bat_tact_for = array_create(_count, 3); bat_vete_for = array_create(_count, 2); -bat_hire_for = array_create(_count, 3); -bat_libr_for = array_create(_count, 3); -bat_comm_for = array_create(_count, 3); -bat_tech_for = array_create(_count, 3); -bat_term_for = array_create(_count, 3); -bat_hono_for = array_create(_count, 3); +bat_hire_for = array_create(_count, 2); +bat_libr_for = array_create(_count, 2); +bat_comm_for = array_create(_count, 2); +bat_tech_for = array_create(_count, 2); +bat_term_for = array_create(_count, 4); +bat_hono_for = array_create(_count, 2); bat_drea_for = array_create(_count, 5); -bat_rhin_for = array_create(_count, 6); -bat_pred_for = array_create(_count, 7); -bat_landraid_for = array_create(_count, 7); +bat_rhin_for = array_create(_count, 5); +bat_pred_for = array_create(_count, 5); +bat_landraid_for = array_create(_count, 5); bat_landspee_for = array_create(_count, 4); bat_whirl_for = array_create(_count, 1); bat_scou_for = array_create(_count, 1); diff --git a/objects/obj_enunit/Alarm_0.gml b/objects/obj_enunit/Alarm_0.gml index 52dc31ca13..6f8937d9bb 100644 --- a/objects/obj_enunit/Alarm_0.gml +++ b/objects/obj_enunit/Alarm_0.gml @@ -139,7 +139,7 @@ if (!engaged) { var x2 = enemy.x; repeat (instance_number(obj_pnunit) - 1) { - x2 += !flank ? 10 : -10; + x2 += !flank ? -10 : 10; enemy2 = instance_nearest(x2, y, obj_pnunit); if (!target_block_is_valid(enemy2, obj_pnunit)) { continue; @@ -150,21 +150,15 @@ if (!engaged) { } var _back_column_size_value = enemy2.column_size; - if (_back_column_size_value < _column_size_value) { - continue; + + if (!check_column_obstruction(_column_size_value, _back_column_size_value)) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.TARGETING, $"non-AP -> infantry found in back column {obj_ncombat.combat_debugger.resolve_label(enemy2)}, firing"); + scr_shoot(i, enemy2, target_unit_index, "att", "ranged"); + _shot = true; + break; } else { - // Calculate chance of shots passing through to back row - // Higher ratio of back column size to front column size increases pass-through chance - // Maximum chance capped at 40% to ensure some protection remains - var _pass_chance = ((_back_column_size_value / _column_size_value) - 1) * 100; - if (irandom_range(1, 100) < min(_pass_chance, 80)) { - continue; - } + continue; } - obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.TARGETING, $"non-AP -> infantry found in back column {obj_ncombat.combat_debugger.resolve_label(enemy2)}, firing"); - scr_shoot(i, enemy2, target_unit_index, "att", "ranged"); - _shot = true; - break; } } diff --git a/objects/obj_pnunit/Alarm_0.gml b/objects/obj_pnunit/Alarm_0.gml index 9bf15aba4a..2f5f3f1574 100644 --- a/objects/obj_pnunit/Alarm_0.gml +++ b/objects/obj_pnunit/Alarm_0.gml @@ -60,6 +60,12 @@ try { } } + // Speed Force sweeps the whole field - bypass normal targeting/range. + if (wep[i] == "Speed Force" || wep[i] == "Speed Force (Ranged)") { + scr_shoot_spread(i); + continue; + } + var _is_ap = apa[i] > 2; var _dist = point_distance(x, y, enemy.x, enemy.y) / 10; var _mode = engaged ? "melee" : "ranged"; diff --git a/scripts/scr_clean/scr_clean.gml b/scripts/scr_clean/scr_clean.gml index 259f43671e..bff46605f3 100644 --- a/scripts/scr_clean/scr_clean.gml +++ b/scripts/scr_clean/scr_clean.gml @@ -196,26 +196,7 @@ function scr_clean(target_object, target_is_infantry, hostile_shots, hostile_dam } /// @self Asset.GMObject.obj_pnunit -function damage_infantry(_damage_data, _shots, _damage, _hostile_armour_pierce) { - var _armour_mod = 0; - switch (_hostile_armour_pierce) { - case 4: - _armour_mod = 0; - break; - case 3: - _armour_mod = 1.5; - break; - case 2: - _armour_mod = 2; - break; - case 1: - _armour_mod = 3; - break; - default: - _armour_mod = 3; - break; - } - +function damage_infantry(_damage_data, _shots, _damage, _armour_pierce) { // Find valid infantry targets var valid_marines = []; for (var m = 0, l = array_length(unit_struct); m < l; m++) { @@ -225,7 +206,7 @@ function damage_infantry(_damage_data, _shots, _damage, _hostile_armour_pierce) } } - obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_infantry valid_marines={array_length(valid_marines)} shots={_shots} dmg={_damage} ap={_hostile_armour_pierce}"); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_infantry valid_marines={array_length(valid_marines)} shots={_shots} dmg={_damage} ap={_armour_pierce}"); // Apply damage for each shot for (var shot = 0; shot < _shots; shot++) { @@ -243,7 +224,7 @@ function damage_infantry(_damage_data, _shots, _damage, _hostile_armour_pierce) // Apply damage var _shot_luck = roll_dice_chapter(1, 100, "low"); var _modified_damage = 0; - var _marine_armour = marine_ac[marine_index] * _armour_mod; + var _marine_armour = combat_rank_armour(marine_ac[marine_index], _armour_pierce, false); if (_shot_luck == 1) { _modified_damage = _damage - (2 * _marine_armour); } else if (_shot_luck == 100) { @@ -305,26 +286,7 @@ function damage_infantry(_damage_data, _shots, _damage, _hostile_armour_pierce) } /// @self Asset.GMObject.obj_pnunit -function damage_vehicles(_damage_data, _shots, _damage, _hostile_armour_pierce) { - var _armour_mod = 0; - switch (_hostile_armour_pierce) { - case 4: - _armour_mod = 0; - break; - case 3: - _armour_mod = 2; - break; - case 2: - _armour_mod = 4; - break; - case 1: - _armour_mod = 6; - break; - default: - _armour_mod = 6; - break; - } - +function damage_vehicles(_damage_data, _shots, _damage, _armour_pierce) { var veh_index = -1; // Find valid vehicle targets @@ -335,7 +297,7 @@ function damage_vehicles(_damage_data, _shots, _damage, _hostile_armour_pierce) } } - obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_vehicles valid_vehicles={array_length(valid_vehicles)} shots={_shots} dmg={_damage} ap={_hostile_armour_pierce}"); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_vehicles valid_vehicles={array_length(valid_vehicles)} shots={_shots} dmg={_damage} ap={_armour_pierce}"); // Apply damage for each hostile shot, until we run out of targets for (var shot = 0; shot < _shots; shot++) { @@ -349,7 +311,8 @@ function damage_vehicles(_damage_data, _shots, _damage, _hostile_armour_pierce) veh_index = array_random_element(valid_vehicles); // Apply damage - var _modified_damage = _damage - veh_ac[veh_index] * _armour_mod; + var _veh_armour = combat_rank_armour(veh_ac[veh_index], _armour_pierce, false); + var _modified_damage = _damage - _veh_armour; if (_modified_damage < 0) { _modified_damage = 0; } @@ -365,7 +328,7 @@ function damage_vehicles(_damage_data, _shots, _damage, _hostile_armour_pierce) veh_dead[veh_index] = 1; _damage_data.units_lost++; obj_ncombat.player_forces -= 1; - obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_vehicles veh[{veh_index}] ({_damage_data.unit_type}) DESTROYED: armour={veh_ac[veh_index] * _armour_mod} raw_dmg={_damage} mod_dmg={_modified_damage} hp_before={_hp_before}"); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"damage_vehicles veh[{veh_index}] ({_damage_data.unit_type}) DESTROYED: armour={_veh_armour} raw_dmg={_damage} mod_dmg={_modified_damage} hp_before={_hp_before}"); // Record loss var existing_index = array_get_index(lost, veh_type[veh_index]); diff --git a/scripts/scr_player_combat_weapon_stacks/scr_player_combat_weapon_stacks.gml b/scripts/scr_player_combat_weapon_stacks/scr_player_combat_weapon_stacks.gml index 0b0b665e8b..d472250a79 100644 --- a/scripts/scr_player_combat_weapon_stacks/scr_player_combat_weapon_stacks.gml +++ b/scripts/scr_player_combat_weapon_stacks/scr_player_combat_weapon_stacks.gml @@ -182,6 +182,16 @@ function scr_player_combat_weapon_stacks() { } } } + if (is_struct(mobi_item) && mobi_item.has_tag("bike")) { + var _speed_force = unit.speed_force(mobi_item.has_tag("sf_ranged")); + var stack_index = find_stack_index(_speed_force.name, head_role, unit); + if (stack_index > -1) { + add_data_to_stack(stack_index, _speed_force, false, head_role, unit); + if (head_role) { + player_head_role_stack(stack_index, unit); + } + } + } if (is_struct(mobi_item)) { add_second_profiles_to_stack(mobi_item); diff --git a/scripts/scr_punit_combat_heplers/scr_punit_combat_heplers.gml b/scripts/scr_punit_combat_heplers/scr_punit_combat_heplers.gml index 55912ec98f..b300865b20 100644 --- a/scripts/scr_punit_combat_heplers/scr_punit_combat_heplers.gml +++ b/scripts/scr_punit_combat_heplers/scr_punit_combat_heplers.gml @@ -311,7 +311,7 @@ function pick_target_column(target_type, mode) { _x2 += 10; var _enemy2 = instance_nearest(_x2, y, obj_enunit); - if (has_target_type(_enemy2, target_type, mode)) { + if (has_target_type(_enemy2, target_type, mode) && check_column_obstruction(enemy.column_size, _enemy2.column_size)) { return _enemy2; } } @@ -323,3 +323,16 @@ function pick_target_column(target_type, mode) { return undefined; } } + +function check_column_obstruction(front_size, back_size) { + if (back_size < front_size) { + return true; + } else { + var _pass_chance = ((back_size / front_size) - 1) * 100; + if (irandom_range(1, 100) < min(_pass_chance, 80)) { + return true; + } + } + + return false; +} diff --git a/scripts/scr_shoot/scr_shoot.gml b/scripts/scr_shoot/scr_shoot.gml index 4ba3f2a3d0..156b12e0c6 100644 --- a/scripts/scr_shoot/scr_shoot.gml +++ b/scripts/scr_shoot/scr_shoot.gml @@ -1,3 +1,158 @@ +/// @function find_next_alive_rank +/// @description Returns the index of the next living rank (dudes_num > 0 and dudes_hp > 0) in a +/// formation, preferring ranks that match the requested vehicle flag. Returns -1 if +/// none. The dudes_hp > 0 check keeps callers safe from dividing by a rank's HP. +/// @param {Id.Instance.obj_enunit} _block The obj_enunit formation to search. +/// @param {Real} _prefer_vehicle 0/1 to prefer that category, or -1 for any living rank. +/// @returns {Real} +function find_next_alive_rank(_block, _prefer_vehicle) { + if (!instance_exists(_block)) { + return -1; + } + + var _fallback = -1; + for (var f = 1; f <= 30; f++) { + if (_block.dudes_num[f] <= 0 || _block.dudes_hp[f] <= 0) { + continue; + } + + if (_prefer_vehicle == -1 || _block.dudes_vehicle[f] == _prefer_vehicle) { + return f; + } + + if (_fallback == -1) { + _fallback = f; + } + } + + return _fallback; +} + +/// @function get_next_enemy_formation +/// @description Returns the nearest enemy formation (obj_enunit) sitting behind the given one +/// that still contains at least one living rank, or noone if there isn't one. +/// @param {Id.Instance} _block The formation we are spilling out of. +/// @returns {Id.Instance} +function get_next_enemy_formation(_block) { + if (!instance_exists(_block)) { + return noone; + } + + var _bx = _block.x; + var _bid = _block.id; + var _best = noone; + var _best_x = 0; + with (obj_enunit) { + if (id == _bid) { + continue; + } + + if (x <= _bx) { + continue; + } + + if (find_next_alive_rank(id, -1) == -1) { + continue; + } + + if (_best == noone || x < _best_x) { + _best = id; + _best_x = x; + } + } + + return _best; +} + +/// @param {Array|Id.Instance} _list Formation ID or array of such to compress & destroy if empty +function combat_cleanup_formations(_list) { + if (is_array(_list)) { + for (var i = 0; i < array_length(_list); i++) { + if (instance_exists(_list[i])) { + compress_enemy_array(_list[i]); + destroy_empty_column(_list[i]); + } + } + } else if (instance_exists(_list)) { + compress_enemy_array(_list); + destroy_empty_column(_list); + } +} + +/// @param {Real} _ac Armour points value +/// @param {Real} _ap Armour-pierce value +/// @param {Bool} _is_vehicle Vehicle or not +/// @returns {Real} Armour value after AP multiplier +function combat_rank_armour(_ac, _ap, _is_vehicle) { + if (_ap < 1 || _ap > 4) { + return _ac; + } + + static _inf = [ + 1, + 3, + 2, + 1.5, + 0, + ]; + + static _veh = [ + 1, + 6, + 4, + 2, + 0, + ]; + + var _armor_mod = _is_vehicle ? _veh[_ap] : _inf[_ap]; + _ac *= _armor_mod; + return round(_ac); +} + +/// @description Computes damage against a rank, applies casualties, and returns the combat outcome. +/// @param {Id.Instance.obj_enunit} block +/// @param {Real} rank +/// @param {Real} dmg_per_weapon +/// @param {Real} ap +/// @param {Real} splash +/// @param {Real} shots_fired +/// @returns {Struct} +function combat_apply_rank_damage(block, rank, dmg_per_weapon, ap, splash, shots_fired) { + var _rank_armour = combat_rank_armour(block.dudes_ac[rank], ap, block.dudes_vehicle[rank]); + var _rank_num = block.dudes_num[rank]; + var _rank_hp = block.dudes_hp[rank]; + var _rank_dr = block.dudes_dr[rank]; + + var _final_hit = max(0, (dmg_per_weapon - (_rank_armour)) * _rank_dr); + var _total_damage = shots_fired * _final_hit; + var _raw_kills = floor(_total_damage / _rank_hp); + var _casualties = min(_raw_kills, _rank_num); + _casualties = min(_casualties, ceil(shots_fired * splash)); + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"combat_apply_rank_damage block={obj_ncombat.combat_debugger.resolve_label(block)} rank={rank} dmg_per_weapon={dmg_per_weapon} ap={ap} splash={splash} shots_fired={shots_fired} -> armour={_rank_armour} final_hit={_final_hit} raw_kills={_raw_kills} casualties={_casualties} bounced={_final_hit <= 0} num_before={_rank_num} hp_before={_rank_hp}"); + + // Apply casualties and update overall combat status + if (_casualties > 0) { + block.dudes_num[rank] -= _casualties; + obj_ncombat.enemy_forces -= _casualties; + } else if (_rank_num == 1 && _total_damage > 0) { + // Handle partial damage for single unit remains + block.dudes_hp[rank] -= _total_damage; + if (block.dudes_hp[rank] <= 0) { + block.dudes_num[rank] = 0; + obj_ncombat.enemy_forces -= 1; + _casualties = 1; // Count as casualty for tracking purposes + } + } + + return { + casualties: _casualties, + raw_kills: _raw_kills, + final_hit: _final_hit, + bounced: (_final_hit <= 0), + }; +} + /// @self Id.Instance.obj_enunit|Id.Instance.obj_pnunit /// @param {Real} weapon_index_position Weapon number /// @param {Id.Instance.obj_enunit|Id.Instance.obj_pnunit} target_object Target object @@ -6,503 +161,424 @@ /// @param {String} melee_or_ranged melee or ranged function scr_shoot(weapon_index_position, target_object, target_type, damage_data, melee_or_ranged) { try { - // This massive clusterfuck of a script uses the newly determined weapon and target data to attack and assign damage - for (var j = 1; j <= 100; j++) { - obj_ncombat.dead_ene[j] = ""; - obj_ncombat.dead_ene_n[j] = 0; - } - obj_ncombat.dead_enemies = 0; - - var hostile_type; - var hostile_damage; - var hostile_weapon; - var hostile_range; - var hostile_splash; - var aggregate_damage = att[weapon_index_position]; - var armour_pierce = apa[weapon_index_position]; if (obj_ncombat.wall_destroyed == 1) { exit; } - if ((weapon_index_position >= 0) && instance_exists(target_object) && (owner == 2)) { - var shots_fired = wep_num[weapon_index_position]; - if (shots_fired == 0 || ammo[weapon_index_position] == 0) { - exit; - } - var doom = 0; - if ((shots_fired != 1) && (melee_or_ranged != "melee")) { - switch (obj_ncombat.enemy) { - case eFACTION.ECCLESIARCHY: - doom = 0.3; - break; - case eFACTION.ELDAR: - doom = 0.4; - break; - case eFACTION.ORK: - doom = 0.2; - break; - case eFACTION.TAU: - doom = 0.4; - break; - case eFACTION.TYRANIDS: - doom = 0.4; - break; - } - } - if (obj_ncombat.enemy == eFACTION.HERETICS) { - aggregate_damage = round(aggregate_damage * 1.15); + if (!instance_exists(target_object)) { + exit; + } + + var _aggregate_damage = att[weapon_index_position]; + var _armour_pierce = apa[weapon_index_position]; + + if ((owner == 2) && (weapon_index_position >= 0)) { + scr_shoot_enemy(weapon_index_position, target_object, damage_data, melee_or_ranged, _aggregate_damage, _armour_pierce); + } + + if (owner == eFACTION.PLAYER) { + scr_shoot_player(weapon_index_position, target_object, target_type, _aggregate_damage, _armour_pierce); + } + } catch (_exception) { + ERROR_HANDLER.handle_exception(_exception); + } +} + +/// @self Id.Instance.obj_enunit +/// @description Handles enemy-side shooting (owner == 2). +/// @param {Id.Instance.obj_pnunit} target_object +function scr_shoot_enemy(weapon_index_position, target_object, damage_data, melee_or_ranged, aggregate_damage, armour_pierce) { + var _hit_count = wep_num[weapon_index_position]; + if (_hit_count == 0 || ammo[weapon_index_position] == 0) { + exit; + } + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_enemy self={obj_ncombat.combat_debugger.resolve_label(id)} weapon_idx={weapon_index_position} weapon={wep[weapon_index_position]} target={obj_ncombat.combat_debugger.resolve_label(target_object)} dmg_data={damage_data} mode={melee_or_ranged} agg_dmg={aggregate_damage} ap={armour_pierce} hits={_hit_count} ammo_before={ammo[weapon_index_position]}"); + + ammo[weapon_index_position] -= 1; + + var _hostile_type = 0; + var _damage = 0; + var _damage_per_weapon = 0; + var _damage_type = ""; + var _weapon_name = wep[weapon_index_position]; + var _weapon_splash = max(1, splash[weapon_index_position]); + var _weapon_range = range[weapon_index_position]; + var _doom_mod = 1; + + // Determine doom modifier based on faction + if (_hit_count > 1 && melee_or_ranged != "melee") { + switch (obj_ncombat.enemy) { + case eFACTION.ECCLESIARCHY: + _doom_mod = 0.3; + break; + case eFACTION.ELDAR: + _doom_mod = 0.4; + break; + case eFACTION.ORK: + _doom_mod = 0.2; + break; + case eFACTION.TAU: + _doom_mod = 0.4; + break; + case eFACTION.TYRANIDS: + _doom_mod = 0.4; + break; + } + } + + // Heretic damage boost + if (obj_ncombat.enemy == eFACTION.HERETICS) { + aggregate_damage = round(aggregate_damage * 1.15); + } + + // Resolve damage type + if (damage_data == "medi") { + _damage_type = (aggregate_damage < armour_pierce) ? "arp" : "att"; + } else { + _damage_type = damage_data; + } + + if (_weapon_name == "Web Spinner") { + _damage_type = "status"; + } + + var _is_assorted = wep_owner[weapon_index_position] == "assorted"; + + if (_damage_type == "status") { + if (melee_or_ranged != "wall") { + _hit_count *= _weapon_splash; + + target_object.hostile_shooters = _is_assorted ? 999 : 1; + _damage = 0; + _hostile_type = 1; + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_enemy -> scr_clean(status) target={obj_ncombat.combat_debugger.resolve_label(target_object)} hostile_type={_hostile_type} hits={_hit_count} dmg={_damage} weapon={_weapon_name}"); + scr_clean(target_object, _hostile_type, _hit_count, _damage, _weapon_name, _weapon_range, _weapon_splash, armour_pierce); + } + } else if (_damage_type == "att" && aggregate_damage > 0) { + _damage_per_weapon = aggregate_damage; + + if (melee_or_ranged == "melee") { + var _men_limit = (target_object.men - target_object.dreads) * 2; + if (_hit_count > _men_limit) { + _doom_mod = _men_limit / _hit_count; } - if ((obj_ncombat.enemy == eFACTION.CHAOS) && (obj_ncombat.threat == 7)) { - doom = 1; + } + + if (_hit_count > 1) { + _damage_per_weapon = floor(_doom_mod * _damage_per_weapon); + _hit_count = floor(_hit_count * _doom_mod); + } + + if (melee_or_ranged != "wall") { + _hit_count *= _weapon_splash; + + if (_hit_count > 0) { + target_object.hostile_shooters = _is_assorted ? 999 : 1; + _damage = _damage_per_weapon / _hit_count; + _hostile_type = 1; + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_enemy -> scr_clean(att) target={obj_ncombat.combat_debugger.resolve_label(target_object)} hostile_type={_hostile_type} hits={_hit_count} dmg_per_shot={_damage} weapon={_weapon_name} ap={armour_pierce}"); + scr_clean(target_object, _hostile_type, _hit_count, _damage, _weapon_name, _weapon_range, _weapon_splash, armour_pierce); } + } + } else if ((_damage_type == "arp" || _damage_type == "dread") && armour_pierce > 0) { + _damage_per_weapon = (aggregate_damage == 0) ? _hit_count : aggregate_damage; - var damage_type = ""; - var stop = 0; + if (melee_or_ranged != "wall") { + _hit_count *= _weapon_splash; + } - if (ammo[weapon_index_position] > 0) { - ammo[weapon_index_position] -= 1; + if (melee_or_ranged == "melee") { + var _veh_limit = (target_object.veh + target_object.dreads) * 5; + if (_hit_count > _veh_limit) { + _doom_mod = _veh_limit / _hit_count; } + } - if (damage_data == "medi") { - damage_type = "att"; - if (aggregate_damage < armour_pierce) { - damage_type = "arp"; + if (_hit_count > 1) { + _damage_per_weapon = floor(_doom_mod * _damage_per_weapon); + _hit_count = floor(_hit_count * _doom_mod); + } + + if (_damage_per_weapon == 0) { + _damage_per_weapon = _hit_count * _doom_mod; + } + + if (_hit_count > 0) { + _damage = _damage_per_weapon / _hit_count; + + if (melee_or_ranged == "wall") { + var dest = 0; + + _damage -= target_object.ac; + _damage = max(0, _damage); + _damage = round(_damage) * _hit_count; + target_object.hp -= _damage; + + if (target_object.hp <= 0) { + dest = 1; } + + obj_nfort.hostile_weapons = _weapon_name; + obj_nfort.hostile_shots = _hit_count; + obj_nfort.hostile_damage = _damage; + + scr_flavor2(dest, "wall", _weapon_range, _weapon_name, _hit_count, _weapon_splash); } else { - damage_type = damage_data; - } - if (wep[weapon_index_position] == "Web Spinner") { - damage_type = "status"; + target_object.hostile_shooters = _is_assorted ? 999 : 1; + _hostile_type = 0; + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_enemy -> scr_clean(arp) target={obj_ncombat.combat_debugger.resolve_label(target_object)} hostile_type={_hostile_type} hits={_hit_count} dmg_per_shot={_damage} weapon={_weapon_name} ap={armour_pierce}"); + scr_clean(target_object, _hostile_type, _hit_count, _damage, _weapon_name, _weapon_range, _weapon_splash, armour_pierce); } + } + } +} - var attack_count_mod = max(1, splash[weapon_index_position]); +/// @self Id.Instance.obj_pnunit +/// @description Handles player-side shooting (owner == eFACTION.PLAYER). +/// @param {Id.Instance.obj_enunit} target_object +function scr_shoot_player(weapon_index_position, target_object, target_type, aggregate_damage, armour_pierce) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player self={obj_ncombat.combat_debugger.resolve_label(id)} weapon_idx={weapon_index_position} target={obj_ncombat.combat_debugger.resolve_label(target_object)} target_type={target_type} agg_dmg={aggregate_damage} ap={armour_pierce}"); - if ((damage_type == "status") && (stop == 0) && (shots_fired > 0)) { - var damage_per_weapon = 0, hit_number = shots_fired; - if (melee_or_ranged != "wall") { - shots_fired *= attack_count_mod; - } - if ((hit_number > 0) && (melee_or_ranged != "wall") && instance_exists(target_object)) { - if (wep_owner[weapon_index_position] == "assorted") { - target_object.hostile_shooters = 999; - } else if (wep_owner[weapon_index_position] != "assorted") { - target_object.hostile_shooters = 1; - } - hostile_damage = 0; - hostile_weapon = wep[weapon_index_position]; - hostile_type = 1; - hostile_range = range[weapon_index_position]; - hostile_splash = attack_count_mod; - - scr_clean(target_object, hostile_type, hit_number, hostile_damage, hostile_weapon, hostile_range, hostile_splash, armour_pierce); - } - } else if ((damage_type == "att") && (aggregate_damage > 0) && (stop == 0) && (shots_fired > 0)) { - var damage_per_weapon, hit_number; + // Resolve shots fired + var shots_fired = 0; + if (weapon_index_position >= 0) { + if (aggregate_damage <= 0) { + exit; + } - damage_per_weapon = aggregate_damage; + shots_fired = wep_num[weapon_index_position]; + } else if (weapon_index_position < -40) { + // Fixes legacy bug where negative weapon indices would always exit early with 0 shots + shots_fired = 1; + } - if (melee_or_ranged == "melee") { - if (shots_fired > (target_object.men - target_object.dreads) * 2) { - doom = ((target_object.men - target_object.dreads) * 2) / shots_fired; - } - } + if (shots_fired == 0) { + exit; + } - hit_number = shots_fired; + var _hp_count = array_length(target_object.dudes_hp); - if ((doom != 0) && (shots_fired > 1)) { - damage_per_weapon = floor((doom * damage_per_weapon)); - hit_number = floor(hit_number * doom); - } - if (melee_or_ranged != "wall") { - shots_fired *= attack_count_mod; - } + while (target_type < _hp_count && target_object.dudes_hp[target_type] <= 0) { + target_type++; + } + + if (target_type >= _hp_count) { + exit; + } - if ((hit_number > 0) && (melee_or_ranged != "wall") && instance_exists(target_object)) { - if (wep_owner[weapon_index_position] == "assorted") { - target_object.hostile_shooters = 999; - } - if (wep_owner[weapon_index_position] != "assorted") { - target_object.hostile_shooters = 1; - } - hostile_damage = damage_per_weapon / hit_number; - hostile_weapon = wep[weapon_index_position]; - hostile_type = 1; - hostile_range = range[weapon_index_position]; - hostile_splash = attack_count_mod; - - scr_clean(target_object, hostile_type, hit_number, hostile_damage, hostile_weapon, hostile_range, hostile_splash, armour_pierce); - } - } else if (((damage_type == "arp") || (damage_type == "dread")) && (armour_pierce > 0) && (stop == 0) && (shots_fired > 0)) { - var damage_per_weapon, hit_number; - damage_per_weapon = aggregate_damage; - if (aggregate_damage == 0) { - damage_per_weapon = shots_fired; - } - if (melee_or_ranged != "wall") { - shots_fired *= attack_count_mod; - } - if (melee_or_ranged == "melee") { - if (shots_fired > ((target_object.veh + target_object.dreads) * 5)) { - doom = ((target_object.veh + target_object.dreads) * 5) / shots_fired; - } - } - hit_number = shots_fired; + var damage_per_weapon = 0; + var attack_count_mod = 1; - if ((doom != 0) && (shots_fired > 1)) { - damage_per_weapon = floor((doom * damage_per_weapon)); - hit_number = floor(hit_number * doom); - } + if (weapon_index_position >= 0) { + var _ammo = ammo[weapon_index_position]; + if (_ammo == 0) { + exit; + } else if (_ammo != -1) { + ammo[weapon_index_position]--; + } - if (damage_per_weapon == 0) { - damage_per_weapon = shots_fired * doom; - } + //? Probably dead code, because `player_silos` seems to never be assigned to after initialization to 0; + if (wep[weapon_index_position] == "Missile Silo") { + obj_ncombat.player_silos -= min(obj_ncombat.player_silos, 30); + } - if (hit_number > 0 && instance_exists(target_object)) { - hostile_weapon = wep[weapon_index_position]; - hostile_range = range[weapon_index_position]; - hostile_splash = attack_count_mod; - hostile_damage = damage_per_weapon / hit_number; - if (melee_or_ranged == "wall") { - var dest = 0; - - hostile_damage -= target_object.ac; - hostile_damage = max(0, hostile_damage); - hostile_damage = round(hostile_damage) * hit_number; - target_object.hp -= hostile_damage; - if (target_object.hp <= 0) { - dest = 1; - } - obj_nfort.hostile_weapons = hostile_weapon; - obj_nfort.hostile_shots = hit_number; - obj_nfort.hostile_damage = hostile_damage; - - scr_flavor2(dest, "wall", hostile_range, hostile_weapon, hit_number, hostile_splash); - } else { - target_object.hostile_shooters = (wep_owner[weapon_index_position] == "assorted") ? 999 : 1; - hostile_type = 0; - - scr_clean(target_object, hostile_type, hit_number, hostile_damage, hostile_weapon, hostile_range, hostile_splash, armour_pierce); - } - } - } + damage_per_weapon = aggregate_damage / wep_num[weapon_index_position]; + attack_count_mod = max(1, splash[weapon_index_position]); + } else if (weapon_index_position < -40) { + attack_count_mod = 3; + + switch (weapon_index_position) { + case -51: + damage_per_weapon = 160; + armour_pierce = 0; + break; + case -52: + damage_per_weapon = 200; + armour_pierce = -1; + break; + case -53: + damage_per_weapon = 250; + armour_pierce = 0; + break; } + } - if (instance_exists(target_object) && (owner == eFACTION.PLAYER)) { - var shots_fired = 0; - var stop = 0; - var damage_type = ""; + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"scr_shoot_player shots={shots_fired} dmg_per_weapon={damage_per_weapon} attack_count_mod={attack_count_mod}"); - if (weapon_index_position >= 0) { - shots_fired = wep_num[weapon_index_position]; - } + // Verify current target rank status + if (target_object.dudes_num[target_type] <= 0) { + var _alive_rank = find_next_alive_rank(target_object, -1); + if (_alive_rank == -1) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.DAMAGE, $"scr_shoot_player target rank dead, no alive ranks -> destroying column"); + destroy_empty_column(target_object); + exit; + } + + target_type = _alive_rank; + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.TARGETING, $"scr_shoot_player target rank was dead, switched to rank {_alive_rank}"); + } + + var _target_block = target_object; + var _target_rank = target_type; + var _shots_left = shots_fired; + var _touched_blocks = [_target_block]; - if (shots_fired == 0) { - exit; + var _first_target = true; + var _primary_flavour = undefined; + var _spill_kills = []; + + // Distribute damage spillover across units + while (_shots_left > 0) { + var _rank_num = _target_block.dudes_num[_target_rank]; + var _rank_hp = _target_block.dudes_hp[_target_rank]; + + var _results = combat_apply_rank_damage(_target_block, _target_rank, damage_per_weapon, armour_pierce, attack_count_mod, _shots_left); + + var _casualties = _results.casualties; + var _final_hit = _results.final_hit; + + var _next_shots = 0; + if (_casualties >= _rank_num && _rank_num > 0 && _results.raw_kills > _rank_num) { + _next_shots = max(0, _shots_left - ceil((_rank_num * _rank_hp) / _final_hit)); + } + + if (_first_target) { + _primary_flavour = scr_flavor(weapon_index_position, _target_block, _target_rank, shots_fired, _casualties, _final_hit <= 0, true); + _first_target = false; + } else if (_casualties > 0) { + array_push(_spill_kills, {name: _target_block.dudes[_target_rank], count: _casualties}); + } + + _shots_left = _next_shots; + if (_shots_left <= 0) { + break; + } + + var _next_rank = find_next_alive_rank(_target_block, _target_block.dudes_vehicle[_target_rank]); + if (_next_rank == -1) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player spillover: no more alive ranks in block {obj_ncombat.combat_debugger.resolve_label(_target_block)}, searching next formation"); + _target_block = get_next_enemy_formation(_target_block); + if (_target_block == noone) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player spillover: no next formation found, done"); + break; } - while (target_type < array_length(target_object.dudes_hp)) { - if (target_object.dudes_hp[target_type] == 0) { - target_type++; - stop = 1; - } else { - stop = 0; - break; - } + array_push(_touched_blocks, _target_block); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player spillover: switched to formation {obj_ncombat.combat_debugger.resolve_label(_target_block)}"); + _next_rank = find_next_alive_rank(_target_block, -1); + if (_next_rank == -1) { + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player spillover: new formation has no alive ranks"); + break; } + } - if (weapon_index_position >= 0) { - if (ammo[weapon_index_position] == 0) { - stop = 1; - } - if (ammo[weapon_index_position] > 0) { - ammo[weapon_index_position] -= 1; + _target_rank = _next_rank; + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_player spillover: continuing at block={obj_ncombat.combat_debugger.resolve_label(_target_block)} rank={_target_rank} shots_left={_shots_left}"); + } + + emit_volley_flavour(_primary_flavour, _spill_kills); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.CLEANUP, $"scr_shoot_player cleanup {array_length(_touched_blocks)} touched blocks, spill_kills={array_length(_spill_kills)}"); + combat_cleanup_formations(_touched_blocks); +} + +/// @self Asset.GMObject.obj_pnunit +/// @description Speed Force: sweep the whole enemy force, dividing damage proportionally to rank +/// size, and report it as ONE consolidated volley line (see emit_volley_flavour). +/// @param {Real} weapon_index_position The Speed Force weapon stack index. +function scr_shoot_spread(weapon_index_position) { + try { + if (wep_num[weapon_index_position] <= 0 || ammo[weapon_index_position] == 0) { + exit; + } + + var _shots = wep_num[weapon_index_position]; + var _ap = apa[weapon_index_position]; + var _damage_per_weapon = att[weapon_index_position] / _shots; + var _splash = max(1, splash[weapon_index_position]); + + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.SHOOTING, $"scr_shoot_spread self={obj_ncombat.combat_debugger.resolve_label(id)} weapon_idx={weapon_index_position} weapon={wep[weapon_index_position]} shots={_shots} ap={_ap} dmg_per_weapon={_damage_per_weapon} splash={_splash}"); + + if (ammo[weapon_index_position] > 0) { + ammo[weapon_index_position] -= 1; + } + + var _formations = []; + var _total = 0; + with (obj_enunit) { + array_push(_formations, id); + for (var _rank = 1; _rank <= 30; _rank++) { + if (dudes[_rank] != "" && dudes_num[_rank] > 0 && dudes_hp[_rank] > 0) { + _total += dudes_num[_rank]; } } - if (wep[weapon_index_position] == "Missile Silo") { - obj_ncombat.player_silos -= min(obj_ncombat.player_silos, 30); - } + } + + if (_total <= 0) { + exit; + } - if (damage_data != "highest") { - damage_type = damage_data; + var _hits = []; + var _wounded = undefined; + for (var fi = 0; fi < array_length(_formations); fi++) { + var _block = _formations[fi]; + if (!instance_exists(_block)) { + continue; } - if ((damage_data == "highest") && (weapon_index_position >= 0)) { - damage_type = "att"; - if ((aggregate_damage >= 100) && (armour_pierce > 0)) { - damage_type = "arp"; + + for (var _rank = 1; _rank <= 30; _rank++) { + if (_block.dudes[_rank] == "" || _block.dudes_num[_rank] <= 0 || _block.dudes_hp[_rank] <= 0) { + continue; } - } - if (damage_data == "highest") { - if (weapon_index_position == -51 || weapon_index_position == -52 || weapon_index_position == -53) { - damage_type = "att"; + + var _rank_shots = _shots * (_block.dudes_num[_rank] / _total); + var _results = combat_apply_rank_damage(_block, _rank, _damage_per_weapon, _ap, _splash, _rank_shots); + + if (_results.casualties > 0) { + array_push(_hits, {name: _block.dudes[_rank], kills: _results.casualties, bounced: _results.bounced, block: _block, rank: _rank}); + } else if (_wounded == undefined) { + _wounded = { + bounced: _results.bounced, + block: _block, + rank: _rank, + }; } } + } - if ((weapon_index_position >= 0) || (weapon_index_position < -40)) { - // Normal shooting - var overkill = 0, damage_remaining = 0, shots_remaining = 0; - - var that_works = false; - - if (weapon_index_position >= 0) { - if ((aggregate_damage > 0) && (stop == 0)) { - that_works = true; - } - } - if ((weapon_index_position < -40) && (stop == 0)) { - that_works = true; + // Generate and emit flavor text + var _primary = undefined; + var _spill = []; + if (array_length(_hits) > 0) { + var _best = 0; + for (var i = 1; i < array_length(_hits); i++) { + if (_hits[i].kills > _hits[_best].kills) { + _best = i; } + } - if (that_works == true) { - var damage_per_weapon = 0, c = 0, target_armour_value = 0, ap = 0, wii = ""; - var attack_count_mod = 0; - - if (weapon_index_position >= 0) { - damage_per_weapon = aggregate_damage / wep_num[weapon_index_position]; - ap = armour_pierce; - } // Average damage - if (weapon_index_position < -40) { - wii = ""; - attack_count_mod = 3; - - if (weapon_index_position == -51) { - wii = "Heavy Bolter Emplacement"; - at = 160; - armour_pierce = 0; - } - if (weapon_index_position == -52) { - wii = "Missile Launcher Emplacement"; - at = 200; - armour_pierce = -1; - } - if (weapon_index_position == -53) { - wii = "Missile Silo"; - at = 250; - armour_pierce = 0; - } - } - - target_armour_value = target_object.dudes_ac[target_type]; - - // Calculate final armor value based on armor piercing (AP) rating against target type - if (target_object.dudes_vehicle[target_type]) { - if (armour_pierce == 4) { - target_armour_value = 0; - } - if (armour_pierce == 3) { - target_armour_value = target_armour_value * 2; - } - if (armour_pierce == 2) { - target_armour_value = target_armour_value * 4; - } - if (armour_pierce == 1) { - target_armour_value = target_armour_value * 6; - } - } else { - if (armour_pierce == 4) { - target_armour_value = 0; - } - if (armour_pierce == 3) { - target_armour_value = target_armour_value * 1.5; - } - if (armour_pierce == 2) { - target_armour_value = target_armour_value * 2; - } - if (armour_pierce == 1) { - target_armour_value = target_armour_value * 3; - } - } - - attack_count_mod = max(1, splash[weapon_index_position]); - - final_hit_damage_value = max(0, damage_per_weapon - target_armour_value); //damage armour reduction - - final_hit_damage_value *= target_object.dudes_dr[target_type]; //damage_resistance mod - - c = shots_fired * final_hit_damage_value; // New damage - - var casualties, onceh = 0, ponies = 0; - - casualties = min(floor(c / target_object.dudes_hp[target_type]), shots_fired * attack_count_mod); - - ponies = target_object.dudes_num[target_type]; - if ((target_object.dudes_num[target_type] == 1) && ((target_object.dudes_hp[target_type] - c) <= 0)) { - casualties = 1; - } - - if (target_object.dudes_num[target_type] - casualties < 0) { - overkill = casualties - target_object.dudes_num[target_type]; - damage_remaining = c - (overkill * target_object.dudes_hp[target_type]); - - shots_remaining = round(damage_remaining / damage_per_weapon); - } - - if (target_object.dudes_num[target_type] - casualties < 0) { - casualties = ponies; - } - if (casualties < 0) { - casualties = 0; - } - - if (casualties >= 1) { - var iii = 0, found = 0, openz = 0; - for (iii = 0; iii <= 40; iii++) { - iii += 1; - if (found == 0) { - if ((obj_ncombat.dead_ene[iii] == "") && (openz == 0)) { - openz = iii; - } - if ((obj_ncombat.dead_ene[iii] == target_object.dudes[target_type]) && (found == 0)) { - found = iii; - obj_ncombat.dead_ene_n[obj_ncombat.dead_enemies] += casualties; - } - } - } - if (found == 0) { - obj_ncombat.dead_enemies += 1; - obj_ncombat.dead_ene[openz] = string(target_object.dudes[target_type]); - obj_ncombat.dead_ene_n[openz] = casualties; - } - } - - var k = 0; - if ((damage_remaining > 0) && (shots_remaining > 0)) { - repeat (10) { - if ((damage_remaining > 0) && (shots_remaining > 0)) { - var godd; - godd = 0; - k = target_type; - - // Find similar target in this same group - repeat (10) { - k += 1; - if (godd == 0) { - if ((target_object.dudes_num[k] > 0) && (target_object.dudes_vehicle[k] == target_object.dudes_vehicle[target_type])) { - godd = k; - } - } - } - k = target_type; - if (godd == 0) { - repeat (10) { - k -= 1; - if ((godd == 0) && (k >= 1)) { - if ((target_object.dudes_num[k] > 0) && (target_object.dudes_vehicle[k] == target_object.dudes_vehicle[target_type])) { - godd = k; - } - } - } - } - - // Found damage_per_weapon similar target to get the damage - if ((godd > 0) && (damage_remaining > 0) && (shots_remaining > 0)) { - var a2, b2, c2, target_armour_value2, ap2; - ap2 = damage_remaining; - a2 = damage_per_weapon; // Average damage - - target_armour_value2 = target_object.dudes_ac[godd]; - if (target_object.dudes_vehicle[godd] == 0) { - if (ap2 == 1) { - target_armour_value2 = target_armour_value2 * 3; - } - if (ap2 == 2) { - target_armour_value2 = target_armour_value2 * 2; - } - if (ap2 == 3) { - target_armour_value2 = target_armour_value2 * 1.5; - } - if (ap2 == 4) { - target_armour_value2 = 0; - } - } - if (target_object.dudes_vehicle[godd] == 1) { - if (ap2 == 1) { - target_armour_value2 = target_armour_value2 * 6; - } - if (ap2 == 2) { - target_armour_value2 = target_armour_value2 * 4; - } - if (ap2 == 3) { - target_armour_value2 = target_armour_value2 * 2; - } - } - b2 = a2 - target_armour_value2; - if (b2 <= 0) { - b2 = 0; - } // Average after armour - - c2 = b2 * shots_remaining; // New damage - - var casualties2 = 0; - var onceh2 = 0; - var ponies2 = 0; - if (attack_count_mod <= 1) { - casualties2 = min(floor(c2 / target_object.dudes_hp[godd]), shots_remaining); - } - - if (attack_count_mod > 1) { - casualties2 = floor(c2 / target_object.dudes_hp[godd]); - } - ponies2 = target_object.dudes_num[godd]; - if ((target_object.dudes_num[godd] == 1) && ((target_object.dudes_hp[godd] - c2) <= 0)) { - casualties2 = 1; - } - if (target_object.dudes_num[godd] < casualties2) { - casualties2 = target_object.dudes_num[godd]; - } - if (casualties2 < 1) { - casualties2 = 0; - damage_remaining = 0; - overkill = 0; - shots_remaining = 0; - } - - if ((casualties2 >= 1) && (shots_fired > 0)) { - var iii = 0; - var found = 0; - var openz = 0; - repeat (40) { - iii += 1; - if (found == 0) { - if ((obj_ncombat.dead_ene[iii] == "") && (openz == 0)) { - openz = iii; - } - if ((obj_ncombat.dead_ene[iii] == target_object.dudes[godd]) && (found == 0)) { - found = iii; - obj_ncombat.dead_ene_n[obj_ncombat.dead_enemies] += casualties; - } - } - } - if (found == 0) { - obj_ncombat.dead_enemies += 1; - obj_ncombat.dead_ene[openz] = string(target_object.dudes[godd]); - obj_ncombat.dead_ene_n[openz] = casualties; - } - - target_object.dudes_num[godd] -= casualties2; - obj_ncombat.enemy_forces -= casualties2; - } - } - } - } - } // End repeat 10 - scr_flavor(weapon_index_position, target_object, target_type, shots_fired - wep_rnum[weapon_index_position], casualties); - - if ((target_object.dudes_num[target_type] == 1) && (c > 0)) { - target_object.dudes_hp[target_type] -= c; - } // Need special flavor here for just damaging - - if (casualties >= 1) { - target_object.dudes_num[target_type] -= casualties; - obj_ncombat.enemy_forces -= casualties; - } + for (var i = 0; i < array_length(_hits); i++) { + if (i == _best) { + continue; } + + array_push(_spill, {name: _hits[i].name, count: _hits[i].kills}); } - if (stop == 0) { - compress_enemy_array(target_object); - destroy_empty_column(target_object); + var _p = _hits[_best]; + if (instance_exists(_p.block)) { + _primary = scr_flavor(weapon_index_position, _p.block, _p.rank, _shots, _p.kills, _p.bounced, true); } + } else if (_wounded != undefined && instance_exists(_wounded.block)) { + _primary = scr_flavor(weapon_index_position, _wounded.block, _wounded.rank, _shots, 0, _wounded.bounced, true); } + + emit_volley_flavour(_primary, _spill); + obj_ncombat.combat_debugger.add(eCOMBAT_CATEGORY.CLEANUP, $"scr_shoot_spread cleanup {array_length(_formations)} formations, hits={array_length(_hits)}"); + combat_cleanup_formations(_formations); } catch (_exception) { ERROR_HANDLER.handle_exception(_exception); } diff --git a/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml b/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml index 36bcf1ad74..4daf71ed0d 100644 --- a/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml +++ b/scripts/scr_ui_formation_bars/scr_ui_formation_bars.gml @@ -74,18 +74,18 @@ function scr_ui_formation_bars() { if (temp[4800 + bar] > 10) { bat_deva_for[bar] = 1; bat_assa_for[bar] = 4; - bat_tact_for[bar] = 2; + bat_tact_for[bar] = 3; bat_vete_for[bar] = 2; - bat_hire_for[bar] = 3; - bat_libr_for[bar] = 3; - bat_comm_for[bar] = 3; - bat_tech_for[bar] = 3; - bat_term_for[bar] = 3; - bat_hono_for[bar] = 3; + bat_hire_for[bar] = 2; + bat_libr_for[bar] = 2; + bat_comm_for[bar] = 2; + bat_tech_for[bar] = 2; + bat_term_for[bar] = 4; + bat_hono_for[bar] = 2; bat_drea_for[bar] = 5; - bat_rhin_for[bar] = 6; - bat_pred_for[bar] = 7; - bat_landraid_for[bar] = 7; + bat_rhin_for[bar] = 5; + bat_pred_for[bar] = 5; + bat_landraid_for[bar] = 5; bat_landspee_for[bar] = 4; bat_whirl_for[bar] = 1; bat_scou_for[bar] = 1; diff --git a/scripts/scr_ui_settings/scr_ui_settings.gml b/scripts/scr_ui_settings/scr_ui_settings.gml index a4a06bb21c..fbdfbe3be0 100644 --- a/scripts/scr_ui_settings/scr_ui_settings.gml +++ b/scripts/scr_ui_settings/scr_ui_settings.gml @@ -529,18 +529,18 @@ function scr_ui_settings() { bat_formation_type[formating] = 1; bat_deva_for[formating] = 1; bat_assa_for[formating] = 4; - bat_tact_for[formating] = 2; + bat_tact_for[formating] = 3; bat_vete_for[formating] = 2; - bat_hire_for[formating] = 3; - bat_libr_for[formating] = 3; - bat_comm_for[formating] = 3; - bat_tech_for[formating] = 3; - bat_term_for[formating] = 3; - bat_hono_for[formating] = 3; + bat_hire_for[formating] = 2; + bat_libr_for[formating] = 2; + bat_comm_for[formating] = 2; + bat_tech_for[formating] = 2; + bat_term_for[formating] = 4; + bat_hono_for[formating] = 2; bat_drea_for[formating] = 5; - bat_rhin_for[formating] = 6; - bat_pred_for[formating] = 7; - bat_landraid_for[formating] = 7; + bat_rhin_for[formating] = 5; + bat_pred_for[formating] = 5; + bat_landraid_for[formating] = 5; bat_landspee_for[formating] = 4; bat_whirl_for[formating] = 1; bat_scou_for[formating] = 1;