Ranged combat: Archery (bow) — projectile, line-of-sight, ranged range #111
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo#111
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of the Alpha epic (pillar 3). Combat is melee-only (
CombatSystem.InRange= melee range). Add ranged combat so PvP has real variety: a bow that hits at distance, requires line of sight, and is dodgeable.Scope
BaseWeapon(melee weapons = 1; bow = N);CombatSystemuses it instead of a fixed melee range.Archeryskill and a bow (BaseWeaponsubclass) driving hit chance via Archery.ResolveDuebehavior andBoltSpell).ProtocolVersion.Currentif the wire changes).Out of scope
Definition of Done
Base DoD applies on top.
CombatSystem).Note (from the arena LoS discussion): server-side attack line-of-sight is now centralized for spells in the new
TargetedSpellbase (one place does target+range+LoS; bolts derive from it). When Archery lands here, build the ranged path to reuse a shared reach check (range + LoS) across melee / bolt / arrow rather than re-implementing it — the same 'no hitting through a wall' rule the arena cover depends on. Don't duplicate the LoS check per attack type.Design review — agreed (ranged combat / Archery)
Owner decisions: bow range 8 (≤ AoI radius); LOS checked unconditionally in the combat path (no-op for melee, since adjacent tiles have no interior cell); a single generic
Projectilewire message serves both spells and ranged (replacesSpellBolt); ammo/arrow-item stays out of scope (infinite arrows for alpha) — we only ship the bow item art + the flying-arrow projectile effect art.Recommendation (build)
SkillName— appendArchery;SkillCatalog— add its row (auto-seeds on new players; no save-version bump — enum appended at the end keeps ordinals stable).BaseWeapon— addabstract int Range.Sword/Greatswordreturn1. NewBow : BaseWeapon(Kind "bow",TwoHanded,Skill => Archery,Range => 8, own damage/cooldown, real bowGraphic). Auto-discovered byItemCatalog;/give bowworks with no command change.Mobile— projectedWeaponRange(unarmed baseline1), set byArm(...). Derived, re-applied byRestoreEquipped; not persisted.CombatSystem— injectTileMap;InRangecomparesattacker.WeaponRange; add unconditionalHasLineOfSightgate in bothTryStartSwingandResolveDue. Carry a ranged/projectile marker on the attack outcome so transport emits the projectile.Shared/Protocol—Projectile(Guid SourceId, Guid TargetId, string Art, int Damage, bool Hit)replacingSpellBolt; art is a data-driven id string (no client type-switch). BumpProtocolVersion.Current10 → 11.WorldTick— on ranged swing resolution emitProjectile(Art="arrow_shot")toPlayersWithin(attackerPosition, radius); spells migrateSpellBolt→Projectile(Art="fire_bolt"/"water_bolt"). Melee keepsAttacked.SpellBolt→Projectile, VFX draws the effect by art id; projectile timing/interp inClient.Core.wood_bow,arrow_shot), updatedocs/uo-asset-map.md.Invariants Check
Attack(TargetId); server decides range from equipped weapon, validates Chebyshev range + LOS at start AND re-validates at resolution. Pending-swing guard + cooldown throttle the intent./give bowreuses admin-gatedGiveCommand.SpellBoltwithProjectilechanges the spell wire and adds ranged ⇒ bumpProtocolVersion.Current10→11 same change; type inShared/Protocol.OutOfRange/NoLineOfSightif any feedback added).CombatSystemgains no lock; injectedTileMapread-only.CombatSystem;World.TryStartSwingstays a delegate; map passed via ctor.SpellBolt→Projectile; arrow animation in the VFX component; nocase/draw pass inGameScreen.Client.Core, unit-tested; only the draw call in MonoGameClient.CombatSystem/Spells; wire + AoI send inWorldTick/Networking.WeaponRangeprojected onto theMobileviaArm(...), no side dict; Archery in the mobile'sSkillSet.Projectilecarries a data-driven art-id string (client drawsstatic/<art>generically, no per-type switch); bow = auto-discovered subclass; Archery = enum + one catalog row.PendingSwing.ResolveAtTick; can't be client-compressed; projectile publicly observable within AoI.Item(kind string, auto-discovered), noItemversion bump; Archery serializes in the existing count-prefixed skill list, noPlayerMobileversion bump (append enum at end; old saves seed default). No cross-player refs, no tick-loop IO.CombatOptions; noIConfigurationread.Projectileis a transient event, sent only toPlayersWithin(sourcePos, AoiRadius)at emit time, absolute ids, no HP/deltas, never global. No new reconciled entity →InterestDiffunchanged.assets.isoa; no placeholder shape; missingstatic/<id>simply not drawn.wood_bow,arrow_shot), never barebow/arrow; mirror indocs/uo-asset-map.md.uo-asset-map.md; DoD already in issue;gm-commands.mdunchanged (/givedata-driven).Server-side validation (trust boundary)
Intent:
Attack(TargetId)(unchanged, no position/range/result). Server validates at swing start: both engageable & alive, no pending swing,ChebyshevDistance ≤ equipped-weapon range,HasLineOfSightvs authoritativeTileMap. Re-validates at resolution: same range + LOS + liveness — a target that broke LOS/range whiffs (no arrow, no damage). Hit is a server RNG roll on Archery vs defender skill; damage applied server-side. Forged/out-of-range/no-LOS targets dropped withcontinue, tick intact.Verification plan
CombatSystem/World) — primary level, no networking:TryStartSwing. [DoD #1]ResolveDue→ whiff; target steps behind a blocking static → whiff; melee at range 1 still lands (LOS no-op). [DoD #2]WeaponSkill == Archery,WeaponRange == 8re-derived, Archery value restored; an old blob without Archery loads with the seeded default (no version bump).ProjectilereplacesSpellBoltwithout breaking fire/water bolt resolution + damage display.CreateWebSocketClient) — in-AoI observer receivesProjectile(+Attackedfor melee); out-of-AoI receives nothing; stale-version client rejected 426. [DoD #3]world.sav*+ Auth/game DBs). Two clients:/give bow→ equip →/setskill archery 1000, position at range with clear LOS,Attack→ capture (a) arrow in flight, (b) hit + floating damage; reposition behind the arena wall → capture (c) the shot whiffing (no arrow / no damage). Flag if the harness can't yet drive equip+attack across two clients.