bugfix - #76
Conversation
📝 WalkthroughWalkthroughPR добавляет гибридную систему боеприпасов для оружия. Она поддерживает магазин, баллистические патроны, заряд батареи и счётчик боеприпасов. Также обновлены прототипы гауссового оружия, снарядов, дробовиков и силовой брони. ChangesГибридная система оружия
Параметры силовой брони
Прочие изменения прототипов
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant GunSystem
participant HybridAmmoProviderComponent
participant Magazine
participant Battery
participant Projectile
GunSystem->>HybridAmmoProviderComponent: запрашивает магазин и количество боеприпасов
HybridAmmoProviderComponent->>Magazine: проверяет баллистические патроны
HybridAmmoProviderComponent->>Battery: проверяет и расходует заряд
HybridAmmoProviderComponent->>Projectile: создаёт снаряд
HybridAmmoProviderComponent->>GunSystem: обновляет состояние и счётчик
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
Content.Server/Weapons/Ranged/Systems/GunSystem.cs (2)
183-197: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Dirtyвызывается после удаления сущности.Если
cartridge.DeleteOnSpawnравноtrue, то на строке 184 сущность удаляется черезDel(ent.Value). Затем строка 196 вызываетDirty(ent!.Value, cartridge)для уже удалённой сущности. Это приводит к ошибке или к работе с невалиднымEntityUid.Вызывайте
Dirtyдо удаления или пропускайте его для удалённых сущностей.🐛 Предлагаемое исправление
SetCartridgeSpent(ent.Value, cartridge, true); + Dirty(ent.Value, cartridge); + if (cartridge.DeleteOnSpawn) Del(ent.Value); } else { userImpulse = false; Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); } + if (Deleted(ent!.Value)) + break; + // Something like ballistic might want to leave it in the container still if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value)) EjectCartridge(ent.Value, angle); - - Dirty(ent!.Value, cartridge); break;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Content.Server/Weapons/Ranged/Systems/GunSystem.cs` around lines 183 - 197, Update the cartridge handling flow around Del and Dirty so Dirty is not called with an entity that has already been deleted. Move Dirty before Del(ent.Value), or conditionally skip it when cartridge.DeleteOnSpawn is true, while preserving the existing ejection behavior.
176-179: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winПередавайте в событии на патрон только снаряды текущего патрона.
События на строках 176 и 339 поднимаются на разных сущностях. Первое обрабатывает
ChemicalAmmoSystem, второе — подписчики оружия. ОднакоshotProjectilesсодержит снаряды всех итераций. ПоэтомуChemicalAmmoSystemможет повторно обработать предыдущие снаряды и списать раствор текущего патрона некорректно. Используйте отдельный список для каждого патрона, а агрегированный список оставьте для события оружия.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Content.Server/Weapons/Ranged/Systems/GunSystem.cs` around lines 176 - 179, В `GunSystem` разделите списки снарядов по назначению: перед событием `AmmoShotEvent` на сущности текущего патрона передавайте только снаряды этой итерации, создав или очищая отдельный список для каждого патрона. Сохраните `shotProjectiles` как агрегированный список для последующего события оружия на строке 339.Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml (1)
42-49: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winЗамените обычный провайдер для всех оружий с
N14MagazineRifle2mmEC.
N14WeaponGaussRifleиспользуетMagazineAmmoProvider.N14WeaponSniperM72GaussRifleSirius67наследуетChamberMagazineAmmoProviderчерезN14WeaponRifleBaseиBaseWeaponRifle. Эти провайдеры не расходуютBatteryComponent, поэтому оружие стреляет при разряженном магазине.Используйте
HybridAmmoProviderсproto: N14Bullet2mmEC,fireCostиmagazineSlot: gun_magazine. Удалите конфликтующий обычный провайдер.
N14MagazineRifle2mmECOldвнешних ссылок не имеет. Удалите его, если legacy-прототип не нужен.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml` around lines 42 - 49, Замените обычные провайдеры боеприпасов у N14WeaponGaussRifle и унаследованного N14WeaponSniperM72GaussRifleSirius67 на HybridAmmoProvider с proto N14Bullet2mmEC, fireCost и magazineSlot gun_magazine; удалите конфликтующие MagazineAmmoProvider/ChamberMagazineAmmoProvider. Удалите N14MagazineRifle2mmECOld, поскольку внешних ссылок на него нет.
🧹 Nitpick comments (3)
Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/powerarmor.yml (1)
9-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winУдалите или перепишите комментарий о
PowerArmorIntegrity.
N14ClothingOuterPowerArmorT45больше не содержитPowerArmorIntegrity. Комментарий описывает отдельный запас целостности, снятиеArmorComponentи ремонт этой механики. Эти утверждения больше не соответствуют прототипу. В тексте также есть опечаткаrityinteg.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/powerarmor.yml` around lines 9 - 12, Удалите устаревший комментарий о PowerArmorIntegrity над N14ClothingOuterPowerArmorT45 либо перепишите его так, чтобы он описывал только фактически поддерживаемую механику прототипа; уберите упоминания отдельного запаса целостности, снятия ArmorComponent, поглощения урона и ремонта, а также исправьте опечатку rityinteg.Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml (1)
90-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueУберите рабочие заметки из прототипа.
Строка 90 содержит незавершённую заметку
# или BaseMagazineRifle. Строка 108 содержит заметку# УДАЛЯЕМ ProjectileBatteryAmmoProvider. Эти комментарии описывают процесс разработки, а не итоговую конфигурацию.Also applies to: 108-108
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml` at line 90, Remove the unfinished development notes from the ammunition magazine prototype: delete the inline “или BaseMagazineRifle” comment on the parent declaration and the “УДАЛЯЕМ ProjectileBatteryAmmoProvider” comment near ProjectileBatteryAmmoProvider, leaving the functional prototype configuration unchanged.Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Projectiles/impacts.yml (1)
57-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
BulletGaussImpactEffectполностью дублируетBulletPlasmaImpactEffect.Обе сущности используют
plasma.rsiсо состояниемimpact_plasma, одинаковое время жизни и одинаковые компоненты. Разница только в идентификаторе.Есть два решения:
- Задайте гауссовый спрайт, например состояние из
projectiles.rsi, по аналогии сGaussMuzzleFlashEffectна строках 39-55.- Либо используйте
BulletPlasmaImpactEffectвProjectiles/2mmEC.ymlи удалите дубликат.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Projectiles/impacts.yml` around lines 57 - 73, Устраните дублирование между BulletGaussImpactEffect и BulletPlasmaImpactEffect: либо назначьте BulletGaussImpactEffect отдельный гауссовый спрайт и состояние по аналогии с GaussMuzzleFlashEffect, либо замените его использование в 2mmEC.yml на BulletPlasmaImpactEffect и удалите дубликат.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs`:
- Around line 35-104: Update OnHybridTakeAmmo to process up to args.Shots
requests instead of always creating one projectile. Wrap the ammo, energy,
projectile creation, and args.Ammo addition flow in a loop, stopping when
ammunition or battery charge is insufficient; preserve the existing failure
reason and ensure each successful iteration consumes one round, spends FireCost,
and updates the ballistic state appropriately.
- Around line 60-70: Синхронизируйте проверку энергии в обработчике стрельбы с
контрактом HybridAmmoProviderComponent: используйте component.BatteryEntity, а
при его отсутствии — magazineEntity.Value как источник BatteryComponent.
Обновите получение батареи и связанные проверки в текущем участке, чтобы заряд и
списание обращались к выбранной сущности, сохранив существующие причины отказа.
- Around line 72-88: В блоке расходования боеприпаса в GunSystem удалите прямое
изменение ballistic.Entities: передайте последний патрон в Containers.Remove и
проверьте успешность операции. Учитывайте UnspawnedCount только если
контейнерных сущностей нет; при неуспешном удалении не изменяйте счётчики и
обработайте отсутствие боеприпаса через существующий gun-no-ammo путь.
- Line 41: Добавьте ключи локализации gun-no-magazine, gun-no-ammo,
gun-no-battery и gun-not-enough-energy во все необходимые .ftl-файлы, включая
сообщения для оружия и использования в SharedFlamerAmmoSystem.cs, с корректными
переводами и доступностью для Loc.GetString.
In `@Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs`:
- Around line 13-28: Состояние боеприпасов HybridAmmoProviderComponent не
синхронизируется с клиентом. В
Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs:13-28
сделайте Count, Capacity и BatteryEntity единственным источником состояния; в
Content.Client/Weapons/Ranged/Systems/GunSystem.Hybrid.cs:14-22 замените
локальное событие RaiseLocalEvent чтением сетевых полей компонента либо
перенесите GetAmmoCountEvent в общий код; в
Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs:60-70 используйте
component.BatteryEntity для выбора источника заряда, обновляйте Count и Capacity
после выстрела и вызывайте Dirty.
- Around line 13-28: Remove the unused Capacity, Count, and BatteryEntity fields
from HybridAmmoProviderComponent, including their DataField and
AutoNetworkedField attributes. Keep MagazineSlot because it is used for
automatic magazine loading, and do not introduce runtime-only EntityUid state
unless it is actually required; if retained, remove its DataField attribute.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml`:
- Around line 92-100: В конфигурации спрайта удалите компонент PowerCellVisuals,
поскольку его unshadedLayer и baseLayer ссылаются на отсутствующие
PowerCellVisualLayers; сохраните существующие слои enum.GunVisualLayers.Base и
enum.GunVisualLayers.Mag без изменений.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/2mmEC.yml`:
- Around line 20-22: Update the Sprite definition for the projectile so its
comment accurately describes the configured `projectiles2.rsi` `buckshot` state,
or change the `sprite` and `state` values to the intended plasma-bolt asset.
Ensure the documentation and actual sprite configuration consistently identify
the projectile graphic.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml`:
- Around line 826-830: Remove the obsolete N14WeaponSniperM72GaussRifleSirius67
prototype if it is no longer needed; otherwise rename its id to a meaningful
identifier and add suffix: Old so it is clearly distinguished from the new M72
Gauss rifle in spawn menus.
- Around line 901-965: В сущности N14WeaponSniperM72GaussRifleSirius
восстановите настройки базовой винтовки: добавьте GunDamageBonus с бонусом
Piercing 38, а также унаследованные через BaseWeaponRifle компоненты
FollowDistance и GunRequiresWield. Не дублируйте уже явно заданные Sprite, Item,
ContainerContainer, MagazineVisuals и Appearance.
---
Outside diff comments:
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.cs`:
- Around line 183-197: Update the cartridge handling flow around Del and Dirty
so Dirty is not called with an entity that has already been deleted. Move Dirty
before Del(ent.Value), or conditionally skip it when cartridge.DeleteOnSpawn is
true, while preserving the existing ejection behavior.
- Around line 176-179: В `GunSystem` разделите списки снарядов по назначению:
перед событием `AmmoShotEvent` на сущности текущего патрона передавайте только
снаряды этой итерации, создав или очищая отдельный список для каждого патрона.
Сохраните `shotProjectiles` как агрегированный список для последующего события
оружия на строке 339.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml`:
- Around line 42-49: Замените обычные провайдеры боеприпасов у
N14WeaponGaussRifle и унаследованного N14WeaponSniperM72GaussRifleSirius67 на
HybridAmmoProvider с proto N14Bullet2mmEC, fireCost и magazineSlot gun_magazine;
удалите конфликтующие MagazineAmmoProvider/ChamberMagazineAmmoProvider. Удалите
N14MagazineRifle2mmECOld, поскольку внешних ссылок на него нет.
---
Nitpick comments:
In
`@Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/powerarmor.yml`:
- Around line 9-12: Удалите устаревший комментарий о PowerArmorIntegrity над
N14ClothingOuterPowerArmorT45 либо перепишите его так, чтобы он описывал только
фактически поддерживаемую механику прототипа; уберите упоминания отдельного
запаса целостности, снятия ArmorComponent, поглощения урона и ремонта, а также
исправьте опечатку rityinteg.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml`:
- Line 90: Remove the unfinished development notes from the ammunition magazine
prototype: delete the inline “или BaseMagazineRifle” comment on the parent
declaration and the “УДАЛЯЕМ ProjectileBatteryAmmoProvider” comment near
ProjectileBatteryAmmoProvider, leaving the functional prototype configuration
unchanged.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Projectiles/impacts.yml`:
- Around line 57-73: Устраните дублирование между BulletGaussImpactEffect и
BulletPlasmaImpactEffect: либо назначьте BulletGaussImpactEffect отдельный
гауссовый спрайт и состояние по аналогии с GaussMuzzleFlashEffect, либо замените
его использование в 2mmEC.yml на BulletPlasmaImpactEffect и удалите дубликат.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 04195ca6-5621-4d94-8acb-ce450f859725
⛔ Files ignored due to path filters (1)
Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles.rsi/muzzle_gauss.pngis excluded by!**/*.png
📒 Files selected for processing (18)
Content.Client/Weapons/Ranged/Systems/GunSystem.Hybrid.csContent.Client/Weapons/Ranged/Systems/GunSystem.csContent.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.csContent.Server/Weapons/Ranged/Systems/GunSystem.csContent.Shared/Weapons/Ranged/Components/AmmoComponent.csContent.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.csResources/Maps/N14/SunnyvaleSurface.ymlResources/Prototypes/Corvax/Entities/Clothing/OuterClothing/powerarmor.ymlResources/Prototypes/_Misfits/Entities/Objects/Misc/campfire_fuel.ymlResources/Prototypes/_Misfits/Roles/Jobs/NCR/ncr_heavy_trooper.ymlResources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/powerarmor.ymlResources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.ymlResources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/2mmEC.ymlResources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Projectiles/impacts.ymlResources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Shotguns/shotguns.ymlResources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.ymlResources/Prototypes/_Nuclear14/Loadouts/Fractions/NCRLoadout/loadouts_followers.ymlResources/Prototypes/_Nuclear14/tags.yml
💤 Files with no reviewable changes (2)
- Resources/Prototypes/_Misfits/Roles/Jobs/NCR/ncr_heavy_trooper.yml
- Resources/Maps/N14/SunnyvaleSurface.yml
| private void OnHybridTakeAmmo(EntityUid uid, HybridAmmoProviderComponent component, TakeAmmoEvent args) | ||
| { | ||
| // 1. Получаем магазин из слота | ||
| var magazineEntity = GetMagazineEntity(uid); | ||
| if (magazineEntity == null) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-magazine"); | ||
| return; | ||
| } | ||
|
|
||
| // 2. Проверяем патроны (BallisticAmmoProvider) | ||
| if (!TryComp<BallisticAmmoProviderComponent>(magazineEntity.Value, out var ballistic)) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-ammo"); | ||
| return; | ||
| } | ||
|
|
||
| // Получаем текущее количество патронов | ||
| var currentCount = GetBallisticShots(ballistic); | ||
| if (currentCount <= 0) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-ammo"); | ||
| return; | ||
| } | ||
|
|
||
| // 3. Проверяем энергию (BatteryComponent) | ||
| if (!TryComp<BatteryComponent>(magazineEntity.Value, out var battery)) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-battery"); | ||
| return; | ||
| } | ||
| if (battery.CurrentCharge < component.FireCost) | ||
| { | ||
| args.Reason = Loc.GetString("gun-not-enough-energy"); | ||
| return; | ||
| } | ||
|
|
||
| // 4. Тратим патрон: удаляем последний патрон из контейнера или уменьшаем UnspawnedCount | ||
| if (ballistic.Entities.Count > 0) | ||
| { | ||
| var lastEntity = ballistic.Entities[^1]; | ||
| ballistic.Entities.RemoveAt(ballistic.Entities.Count - 1); | ||
| Containers.Remove(lastEntity, ballistic.Container); | ||
| QueueDel(lastEntity); // Удаляем сущность патрона (гильза не нужна) | ||
| } | ||
| else if (ballistic.UnspawnedCount > 0) | ||
| { | ||
| ballistic.UnspawnedCount--; | ||
| } | ||
| else | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-ammo"); | ||
| return; | ||
| } | ||
|
|
||
| // 5. Тратим энергию | ||
| _battery.UseCharge(magazineEntity.Value, component.FireCost); | ||
|
|
||
| // 6. Создаём снаряд в координатах выстрела | ||
| var fromCoordinates = args.Coordinates; | ||
| var mapCoords = fromCoordinates.ToMap(EntityManager, TransformSystem); | ||
| var projectile = Spawn(component.Prototype, mapCoords); | ||
|
|
||
| // 7. Добавляем снаряд в список для выстрела (основной GunSystem обработает его) | ||
| args.Ammo.Add((projectile, EnsureShootable(projectile))); | ||
|
|
||
| // 8. Обновляем счётчик на клиенте | ||
| Dirty(magazineEntity.Value, ballistic); | ||
| UpdateAmmoCount(uid); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Обработчик игнорирует args.Shots.
TakeAmmoEvent.Shots задаёт количество запрошенных выстрелов. Другие провайдеры (например, BallisticAmmoProvider) выдают до Shots единиц боеприпасов за одно событие. Здесь всегда выдаётся ровно один снаряд. При FullAuto или высоком fireRate оружие будет стрелять медленнее ожидаемого.
Оберните шаги 2–7 в цикл по args.Shots и прерывайте его при нехватке патронов или энергии.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs` around lines 35 -
104, Update OnHybridTakeAmmo to process up to args.Shots requests instead of
always creating one projectile. Wrap the ammo, energy, projectile creation, and
args.Ammo addition flow in a loop, stopping when ammunition or battery charge is
insufficient; preserve the existing failure reason and ensure each successful
iteration consumes one round, spends FireCost, and updates the ballistic state
appropriately.
| var magazineEntity = GetMagazineEntity(uid); | ||
| if (magazineEntity == null) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-magazine"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Проверить наличие ключей локализации
for key in gun-no-magazine gun-no-ammo gun-no-battery gun-not-enough-energy; do
echo "=== $key ==="
rg -n "^${key}\s*=" --glob '*.ftl' || echo "НЕ НАЙДЕН"
doneRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 305
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- gun key usages ---'
rg -n --hidden --glob '!*.git/*' 'gun-(no-magazine|no-ammo|no-battery|not-enough-energy)' .
printf '%s\n' '--- localization files and nearby gun entries ---'
git ls-files '*.ftl' | head -80
rg -n -i --glob '*.ftl' 'gun|magazine|battery|enough.energy|no.ammo' . | head -200
printf '%s\n' '--- relevant source context ---'
sed -n '1,110p' Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.csRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 37095
Добавьте ключи локализации для сообщений оружия.
Ключи gun-no-magazine, gun-no-ammo, gun-no-battery и gun-not-enough-energy отсутствуют во всех .ftl. Они также используются в SharedFlamerAmmoSystem.cs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs` at line 41,
Добавьте ключи локализации gun-no-magazine, gun-no-ammo, gun-no-battery и
gun-not-enough-energy во все необходимые .ftl-файлы, включая сообщения для
оружия и использования в SharedFlamerAmmoSystem.cs, с корректными переводами и
доступностью для Loc.GetString.
| // 3. Проверяем энергию (BatteryComponent) | ||
| if (!TryComp<BatteryComponent>(magazineEntity.Value, out var battery)) | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-battery"); | ||
| return; | ||
| } | ||
| if (battery.CurrentCharge < component.FireCost) | ||
| { | ||
| args.Reason = Loc.GetString("gun-not-enough-energy"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Батарея берётся только из магазина.
component.BatteryEntity не используется. Заряд читается из BatteryComponent магазина. Комментарий в HybridAmmoProviderComponent описывает другое поведение: «если null, используем свой uid». Приведите код и комментарий к одному поведению.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs` around lines 60 -
70, Синхронизируйте проверку энергии в обработчике стрельбы с контрактом
HybridAmmoProviderComponent: используйте component.BatteryEntity, а при его
отсутствии — magazineEntity.Value как источник BatteryComponent. Обновите
получение батареи и связанные проверки в текущем участке, чтобы заряд и списание
обращались к выбранной сущности, сохранив существующие причины отказа.
| // 4. Тратим патрон: удаляем последний патрон из контейнера или уменьшаем UnspawnedCount | ||
| if (ballistic.Entities.Count > 0) | ||
| { | ||
| var lastEntity = ballistic.Entities[^1]; | ||
| ballistic.Entities.RemoveAt(ballistic.Entities.Count - 1); | ||
| Containers.Remove(lastEntity, ballistic.Container); | ||
| QueueDel(lastEntity); // Удаляем сущность патрона (гильза не нужна) | ||
| } | ||
| else if (ballistic.UnspawnedCount > 0) | ||
| { | ||
| ballistic.UnspawnedCount--; | ||
| } | ||
| else | ||
| { | ||
| args.Reason = Loc.GetString("gun-no-ammo"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Не изменяйте ballistic.Entities напрямую.
Containers.Remove сам поднимает EntRemovedFromContainerMessage, а BallisticAmmoProviderComponent обрабатывает это сообщение и удаляет сущность из Entities. Здесь список изменяется вручную до вызова Containers.Remove. Если Containers.Remove вернёт false, патрон исчезнет из счётчика, но останется в контейнере. Это рассинхронизирует состояние магазина.
Удаляйте патрон только через контейнер и проверяйте результат.
🐛 Предлагаемое исправление
if (ballistic.Entities.Count > 0)
{
var lastEntity = ballistic.Entities[^1];
- ballistic.Entities.RemoveAt(ballistic.Entities.Count - 1);
- Containers.Remove(lastEntity, ballistic.Container);
+ if (!Containers.Remove(lastEntity, ballistic.Container))
+ {
+ args.Reason = Loc.GetString("gun-no-ammo");
+ return;
+ }
QueueDel(lastEntity); // Удаляем сущность патрона (гильза не нужна)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 4. Тратим патрон: удаляем последний патрон из контейнера или уменьшаем UnspawnedCount | |
| if (ballistic.Entities.Count > 0) | |
| { | |
| var lastEntity = ballistic.Entities[^1]; | |
| ballistic.Entities.RemoveAt(ballistic.Entities.Count - 1); | |
| Containers.Remove(lastEntity, ballistic.Container); | |
| QueueDel(lastEntity); // Удаляем сущность патрона (гильза не нужна) | |
| } | |
| else if (ballistic.UnspawnedCount > 0) | |
| { | |
| ballistic.UnspawnedCount--; | |
| } | |
| else | |
| { | |
| args.Reason = Loc.GetString("gun-no-ammo"); | |
| return; | |
| } | |
| // 4. Тратим патрон: удаляем последний патрон из контейнера или уменьшаем UnspawnedCount | |
| if (ballistic.Entities.Count > 0) | |
| { | |
| var lastEntity = ballistic.Entities[^1]; | |
| if (!Containers.Remove(lastEntity, ballistic.Container)) | |
| { | |
| args.Reason = Loc.GetString("gun-no-ammo"); | |
| return; | |
| } | |
| QueueDel(lastEntity); // Удаляем сущность патрона (гильза не нужна) | |
| } | |
| else if (ballistic.UnspawnedCount > 0) | |
| { | |
| ballistic.UnspawnedCount--; | |
| } | |
| else | |
| { | |
| args.Reason = Loc.GetString("gun-no-ammo"); | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs` around lines 72 -
88, В блоке расходования боеприпаса в GunSystem удалите прямое изменение
ballistic.Entities: передайте последний патрон в Containers.Remove и проверьте
успешность операции. Учитывайте UnspawnedCount только если контейнерных
сущностей нет; при неуспешном удалении не изменяйте счётчики и обработайте
отсутствие боеприпаса через существующий gun-no-ammo путь.
| [DataField("capacity"), AutoNetworkedField] | ||
| public int Capacity = 10; | ||
|
|
||
| [DataField("count"), AutoNetworkedField] | ||
| public int Count = 10; | ||
|
|
||
| [DataField("fireCost")] | ||
| public float FireCost = 100f; | ||
|
|
||
| // Ссылка на батарею (если null, используем свой uid) | ||
| [DataField("battery")] | ||
| public EntityUid? BatteryEntity; | ||
|
|
||
| // Ссылка на слот магазина (для автоматической загрузки патронов) | ||
| [DataField("magazineSlot")] | ||
| public string? MagazineSlot; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Контракт HybridAmmoProviderComponent не реализован: состояние боеприпасов не доходит до клиента. Компонент объявляет сетевые поля Count, Capacity и поле BatteryEntity, но серверная логика их не заполняет и не читает. Из-за этого клиентский счётчик боеприпасов всегда показывает 0.
Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs#L13-L28: либо удалитеCapacity,CountиBatteryEntity, либо сделайте их единственным источником состояния для клиента.Content.Client/Weapons/Ranged/Systems/GunSystem.Hybrid.cs#L14-L22: заменитеRaiseLocalEvent(uid, ref ev, false)на чтение сетевых полей компонента, либо перенесите обработчикGetAmmoCountEventв общий код.Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs#L60-L70: используйтеcomponent.BatteryEntityпри выборе источника заряда и обновляйтеCount/Capacityс вызовомDirtyпосле каждого выстрела.
📍 Affects 3 files
Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs#L13-L28(this comment)Content.Client/Weapons/Ranged/Systems/GunSystem.Hybrid.cs#L14-L22Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs#L60-L70
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs`
around lines 13 - 28, Состояние боеприпасов HybridAmmoProviderComponent не
синхронизируется с клиентом. В
Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs:13-28
сделайте Count, Capacity и BatteryEntity единственным источником состояния; в
Content.Client/Weapons/Ranged/Systems/GunSystem.Hybrid.cs:14-22 замените
локальное событие RaiseLocalEvent чтением сетевых полей компонента либо
перенесите GetAmmoCountEvent в общий код; в
Content.Server/Weapons/Ranged/Systems/GunSystem.Hybrid.cs:60-70 используйте
component.BatteryEntity для выбора источника заряда, обновляйте Count и Capacity
после выстрела и вызывайте Dirty.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Уберите неиспользуемые поля или начните их использовать.
Поля Capacity, Count и BatteryEntity не читаются ни серверным GunSystem.Hybrid.cs, ни клиентским. Счётчик боеприпасов берётся из BallisticAmmoProviderComponent магазина, а заряд — из BatteryComponent магазина. Сетевая синхронизация Capacity и Count создаёт мёртвое состояние: значения из прототипа (10/10) никогда не совпадут с реальным количеством патронов, и будущий код может прочитать их как истину.
Дополнительно: EntityUid в DataField не задаётся из YAML-прототипа. Если поле нужно только в рантайме, объявите его без DataField.
♻️ Предлагаемое изменение
- [DataField("capacity"), AutoNetworkedField]
- public int Capacity = 10;
-
- [DataField("count"), AutoNetworkedField]
- public int Count = 10;
-
[DataField("fireCost")]
public float FireCost = 100f;
- // Ссылка на батарею (если null, используем свой uid)
- [DataField("battery")]
- public EntityUid? BatteryEntity;
+ // Ссылка на батарею (если null, используем магазин)
+ public EntityUid? BatteryEntity;
// Ссылка на слот магазина (для автоматической загрузки патронов)
[DataField("magazineSlot")]
public string? MagazineSlot;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Content.Shared/Weapons/Ranged/Components/HybridAmmoProviderComponent.cs`
around lines 13 - 28, Remove the unused Capacity, Count, and BatteryEntity
fields from HybridAmmoProviderComponent, including their DataField and
AutoNetworkedField attributes. Keep MagazineSlot because it is used for
automatic magazine loading, and do not introduce runtime-only EntityUid state
unless it is actually required; if retained, remove its DataField attribute.
| - type: Sprite | ||
| netsync: false | ||
| sprite: _Nuclear14/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC/2mmriflemag.rsi | ||
| layers: | ||
| - state: base | ||
| map: ["enum.GunVisualLayers.Base"] | ||
| - state: mag-1 | ||
| map: ["enum.GunVisualLayers.Mag"] | ||
| scale: 0.60, 0.60 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
PowerCellVisuals ссылается на несуществующие слои спрайта.
Компонент PowerCellVisuals задаёт unshadedLayer: enum.PowerCellVisualLayers.Unshaded и baseLayer: enum.PowerCellVisualLayers.Base. Слои спрайта на строках 96-99 объявлены с ключами enum.GunVisualLayers.Base и enum.GunVisualLayers.Mag. Слои PowerCellVisualLayers отсутствуют. Система визуализации не найдёт эти слои и выдаст ошибку или проигнорирует отображение заряда.
Удалите PowerCellVisuals либо добавьте соответствующие слои в Sprite.
🐛 Предлагаемое исправление
- type: Appearance
- - type: PowerCellVisuals
- unshadedLayer: enum.PowerCellVisualLayers.Unshaded
- baseLayer: enum.PowerCellVisualLayers.Base
- type: RiggableAlso applies to: 137-141
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Magazines/2mmEC.yml`
around lines 92 - 100, В конфигурации спрайта удалите компонент
PowerCellVisuals, поскольку его unshadedLayer и baseLayer ссылаются на
отсутствующие PowerCellVisualLayers; сохраните существующие слои
enum.GunVisualLayers.Base и enum.GunVisualLayers.Mag без изменений.
| - type: Sprite #Misfits Fix — was using stretched ballistic bullet sprite (projectiles2.rsi 'bullet' scale 1.2,0.5). Now uses the proper plasma bolt from plasma.rsi. | ||
| sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi | ||
| state: buckshot |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Комментарий противоречит конфигурации спрайта.
Комментарий говорит: спрайт теперь берётся из plasma.rsi. Фактически задан sprite: Objects/Weapons/Guns/Projectiles/projectiles2.rsi и state: buckshot. Состояние buckshot — это дробь, а не гауссовая пуля.
Приведите комментарий в соответствие с кодом или укажите нужный спрайт.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/2mmEC.yml`
around lines 20 - 22, Update the Sprite definition for the projectile so its
comment accurately describes the configured `projectiles2.rsi` `buckshot` state,
or change the `sprite` and `state` values to the intended plasma-bolt asset.
Ensure the documentation and actual sprite configuration consistently identify
the projectile graphic.
| - type: entity | ||
| name: M72 Gauss rifle | ||
| parent: N14WeaponRifleBase | ||
| id: N14WeaponSniperM72GaussRifleSirius | ||
| id: N14WeaponSniperM72GaussRifleSirius67 | ||
| description: "An anti-materiel rifle with an ergonomic, sleek, skeletonized design. Simply looking at it can you tell it'll rip some limbs apart. Uses .50 anti-material ammo." |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Идентификатор N14WeaponSniperM72GaussRifleSirius67 выглядит как временная заглушка.
Суффикс 67 не несёт смысла и не соответствует соглашению об именовании в файле. Старая сущность остаётся доступной в меню спавна и дублирует новую по имени «M72 Gauss rifle». Игроки и мапперы не смогут их различить.
Есть два решения:
- Удалите старый прототип, если он больше не нужен.
- Либо дайте ему понятный идентификатор и суффикс
suffix: Old, чтобы отличать его в меню.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml`
around lines 826 - 830, Remove the obsolete N14WeaponSniperM72GaussRifleSirius67
prototype if it is no longer needed; otherwise rename its id to a meaningful
identifier and add suffix: Old so it is clearly distinguished from the new M72
Gauss rifle in spawn menus.
| - type: entity | ||
| name: M72 Gauss rifle | ||
| parent: BaseItem | ||
| id: N14WeaponSniperM72GaussRifleSirius | ||
| description: An advanced gauss rifle that consumes both energy and 2mmEC ammunition. | ||
| components: | ||
| - type: Clothing | ||
| sprite: _Nuclear14/Objects/Weapons/Guns/Snipers/gaussrifle.rsi | ||
| quickEquip: false | ||
| slots: | ||
| - Back | ||
| - suitStorage | ||
| - type: Sprite | ||
| sprite: _Nuclear14/Objects/Weapons/Guns/Snipers/gaussrifle.rsi | ||
| layers: | ||
| - state: base | ||
| map: ["enum.GunVisualLayers.Base"] | ||
| - state: mag-0 | ||
| map: ["enum.GunVisualLayers.Mag"] | ||
| - type: Item | ||
| size: Huge | ||
| - type: Wieldable | ||
| wieldedSpeedModifier: 0.75 | ||
| - type: GunWieldBonus | ||
| minAngle: -23 | ||
| maxAngle: -104 | ||
| - type: HybridAmmoProvider | ||
| proto: N14Bullet2mmEC | ||
| fireCost: 50 | ||
| magazineSlot: gun_magazine | ||
| - type: Gun | ||
| minAngle: 24 | ||
| maxAngle: 60 | ||
| angleIncrease: 4 | ||
| angleDecay: 16 | ||
| fireRate: 1.5 | ||
| soundGunshot: | ||
| path: /Audio/Weapons/Guns/Gunshots/GaussRifle_SingleShot_1.ogg | ||
| soundEmpty: | ||
| path: /Audio/Weapons/Guns/Empty/empty.ogg | ||
| selectedMode: SemiAuto | ||
| availableModes: | ||
| - SemiAuto | ||
| - type: ItemSlots | ||
| slots: | ||
| gun_magazine: | ||
| name: Magazine | ||
| startingItem: N14MagazineRifle2mmEC | ||
| insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg | ||
| ejectSound: /Audio/Weapons/Guns/MagOut/batrifle_magout.ogg | ||
| whitelist: | ||
| tags: | ||
| - N14MagazineRifle2mmEC | ||
| - type: ContainerContainer | ||
| containers: | ||
| gun_magazine: !type:ContainerSlot | ||
| - type: MagazineVisuals | ||
| magState: mag | ||
| steps: 1 | ||
| zeroVisible: true | ||
| - type: Appearance | ||
| - type: AmmoCounter | ||
| - type: StaticPrice | ||
| price: 200 | ||
| # - type: Craftable # #Misfits Remove: Stalker14 crafting system |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Показать состав базового прототипа оружия
rg -n -A 40 'id:\s*N14WeaponRifleBase' --glob '*.yml'Repository: Echo-DreamMaker/Sirius_Fallout
Length of output: 168
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- matching prototypes and references ---'
rg -n -C 8 'N14WeaponRifleBase|N14WeaponSniperM72GaussRifleSirius|GunDamageBonus|FollowDistance|HybridAmmoProvider' \
Resources/Prototypes --glob '*.yml' --glob '*.yaml' | head -n 400
printf '%s\n' '--- candidate base prototype files ---'
rg -l 'N14WeaponRifleBase|GunDamageBonus' Resources/Prototypes --glob '*.yml' --glob '*.yaml' | head -n 100
printf '%s\n' '--- target file context ---'
sed -n '870,980p' Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.ymlRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 45649
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- sniper prototypes around the target ---'
sed -n '1,140p' Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
sed -n '760,930p' Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml
printf '%s\n' '--- all declarations and parent references for rifle bases ---'
rg -n '^[[:space:]-]*(id|parent):.*(N14WeaponRifleBase|N14Weapon.*Rifle.*Base|BaseItem)' \
Resources/Prototypes --glob '*.yml' --glob '*.yaml'
printf '%s\n' '--- repository state and target diff summary ---'
git status --short
git diff --stat -- Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.ymlRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- rifle base definition ---'
sed -n '1,38p' Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
printf '%s\n' '--- relevant base definitions ---'
rg -n -C 12 '^(- type: entity| id: BaseItem| id: BaseWeaponRifle| id: BaseWeaponSniper| id: N14WeaponRifleBase)' \
Resources/Prototypes --glob '*.yml' --glob '*.yaml' | rg -n -A 18 -B 2 \
'BaseItem|BaseWeaponRifle|BaseWeaponSniper|N14WeaponRifleBase' | head -n 240
printf '%s\n' '--- 2mm projectile and damage declarations ---'
rg -n -C 15 'N14Bullet2mmEC|N14Cartridge2mmEC|Piercing: 38|GunDamageBonus' \
Resources/Prototypes/_Nuclear14 Resources/Prototypes/_Misfits --glob '*.yml' --glob '*.yaml' | head -n 320Repository: Echo-DreamMaker/Sirius_Fallout
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- core weapon bases ---'
rg -n -B 3 -A 30 '^ id: (BaseItem|BaseWeaponRifle|BaseWeaponSniper)$' \
Resources/Prototypes --glob '*.yml' --glob '*.yaml'
printf '%s\n' '--- exact 2mm ammunition prototypes ---'
rg -n -B 3 -A 35 '^ id: (N14Bullet2mmEC|N14Cartridge2mmEC)$' \
Resources/Prototypes --glob '*.yml' --glob '*.yaml'
printf '%s\n' '--- all Gauss damage-related declarations ---'
rg -n -B 5 -A 12 'N14WeaponSniperM72GaussRifleSirius|Piercing: 38|N14Bullet2mmEC' \
Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml \
Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition --glob '*.yml' --glob '*.yaml'Repository: Echo-DreamMaker/Sirius_Fallout
Length of output: 30646
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
path = Path("Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml")
text = path.read_text()
def block_for(proto_id):
starts = [m.start() for m in re.finditer(r"(?m)^- type: entity$", text)]
for i, start in enumerate(starts):
end = starts[i + 1] if i + 1 < len(starts) else len(text)
block = text[start:end]
if re.search(rf"(?m)^ id: {re.escape(proto_id)}$", block):
return block
raise SystemExit(f"prototype not found: {proto_id}")
def component_types(block):
return re.findall(r"(?m)^ - type: ([^\s#]+)", block)
ids = [
"N14WeaponRifleBase",
"N14WeaponSniperM72GaussRifleSirius67",
"N14WeaponSniperM72GaussRifleSirius",
]
for proto_id in ids:
block = block_for(proto_id)
parent = re.search(r"(?m)^ parent: (.+)$", block)
print(f"{proto_id}: parent={parent.group(1) if parent else '<none>'}")
print(" components:", ", ".join(component_types(block)))
old = set(component_types(block_for("N14WeaponSniperM72GaussRifleSirius67")))
new = set(component_types(block_for("N14WeaponSniperM72GaussRifleSirius")))
base = set(component_types(block_for("N14WeaponRifleBase")))
print("old-only components:", ", ".join(sorted(old - new)) or "<none>")
print("new-only components:", ", ".join(sorted(new - old)) or "<none>")
print("base components not declared by new:", ", ".join(sorted(base - new)) or "<none>")
print("new has GunDamageBonus:", "GunDamageBonus" in new)
PYRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 208
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
root = Path("Resources/Prototypes")
def find_block(proto_id):
for path in root.rglob("*.yml"):
text = path.read_text(errors="replace")
starts = [m.start() for m in re.finditer(r"(?m)^- type: entity$", text)]
for i, start in enumerate(starts):
end = starts[i + 1] if i + 1 < len(starts) else len(text)
block = text[start:end]
if re.search(rf"(?m)^ id: {re.escape(proto_id)}$", block):
return path, block
raise SystemExit(f"prototype not found: {proto_id}")
def component_types(block):
return re.findall(r"(?m)^ - type: ([^\s#]+)", block)
ids = [
"N14WeaponRifleBase",
"N14WeaponSniperM72GaussRifleSirius67",
"N14WeaponSniperM72GaussRifleSirius",
]
blocks = {}
for proto_id in ids:
path, block = find_block(proto_id)
blocks[proto_id] = block
parent = re.search(r"(?m)^ parent: (.+)$", block)
print(f"{proto_id}: file={path}, parent={parent.group(1) if parent else '<none>'}")
print(" components:", ", ".join(component_types(block)))
old = set(component_types(blocks["N14WeaponSniperM72GaussRifleSirius67"]))
new = set(component_types(blocks["N14WeaponSniperM72GaussRifleSirius"]))
base = set(component_types(blocks["N14WeaponRifleBase"]))
print("old-only components:", ", ".join(sorted(old - new)) or "<none>")
print("new-only components:", ", ".join(sorted(new - old)) or "<none>")
print("base components not declared by new:", ", ".join(sorted(base - new)) or "<none>")
print("new has GunDamageBonus:", "GunDamageBonus" in new)
PYRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 1256
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- BaseWeaponRifle parent chain and wield requirement ---'
sed -n '1,55p' Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
rg -n -B 5 -A 28 'id: BaseGunWieldable|type: GunRequiresWield' Resources/Prototypes --glob '*.yml' --glob '*.yaml'
printf '%s\n' '--- old and new Gauss component sections ---'
sed -n '826,979p' Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.ymlRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 50387
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- complete BaseWeaponRifle declaration ---'
sed -n '1,90p' Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
printf '%s\n' '--- component declarations in the Gauss prototypes ---'
python3 - <<'PY'
from pathlib import Path
import re
paths = [
Path("Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml"),
Path("Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Rifles/rifles.yml"),
Path("Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml"),
]
wanted = {
"BaseWeaponRifle",
"N14WeaponRifleBase",
"N14WeaponSniperM72GaussRifleSirius67",
"N14WeaponSniperM72GaussRifleSirius",
}
for path in paths:
text = path.read_text()
starts = [m.start() for m in re.finditer(r"(?m)^- type: entity$", text)]
for i, start in enumerate(starts):
block = text[start:starts[i + 1] if i + 1 < len(starts) else len(text)]
m = re.search(r"(?m)^ id: ([^\s]+)$", block)
if not m or m.group(1) not in wanted:
continue
print(f"{m.group(1)} ({path})")
for component in re.findall(r"(?m)^ - type: ([^\s#]+)", block):
print(f" - {component}")
PYRepository: Echo-DreamMaker/Sirius_Fallout
Length of output: 3761
Восстановите настройки базовой винтовки.
N14Bullet2mmEC наносит 32 Piercing. Без GunDamageBonus с Piercing: 38 новая сущность наносит 32 вместо 70. Добавьте этот компонент.
Новая сущность также не содержит FollowDistance и GunRequiresWield, который наследуется через BaseWeaponRifle. Sprite, Item, ContainerContainer, MagazineVisuals и Appearance уже заданы явно и не потеряны.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Snipers/snipers.yml`
around lines 901 - 965, В сущности N14WeaponSniperM72GaussRifleSirius
восстановите настройки базовой винтовки: добавьте GunDamageBonus с бонусом
Piercing 38, а также унаследованные через BaseWeaponRifle компоненты
FollowDistance и GunRequiresWield. Не дублируйте уже явно заданные Sprite, Item,
ContainerContainer, MagazineVisuals и Appearance.


Багфикс
🆑 Ravenreaper
Summary by CodeRabbit
Новые возможности
Изменения