diff --git a/tools/RunicNpcTest.cs b/tools/RunicNpcTest.cs index f2fdb27..78bffaf 100644 --- a/tools/RunicNpcTest.cs +++ b/tools/RunicNpcTest.cs @@ -45,7 +45,7 @@ namespace Oxide.Plugins /// a made-up user id (11400000001 and up), so IsNpc is false. They take no damage. /// /// - [Info("RunicNpcTest", "Runic Gateway", "0.7.4")] + [Info("RunicNpcTest", "Runic Gateway", "0.7.5")] [Description("RunicNPC stage 2 to 7 tests, the stage 6 spike and stage 9's cost check. A developer tool: never ship it.")] internal class RunicNpcTest : RustPlugin { @@ -1753,6 +1753,31 @@ namespace Oxide.Plugins return false; } + /// + /// Where a stage 5 fight's second NPC stands: m from the first, on the navmesh, + /// with a clear line between their eyes. North first, as the fights were laid out; then the other seven + /// ways round. On the drill's 3500 map a cliff rock stood between the victim and the bully north of it, + /// so neither ever saw the other and s5.fight.shootBack failed on every run (issue #20). + /// + private Vector3 Partner(Vector3 at, float dist) + { + for (int k = 0; k < 8; k++) + { + float angle = k * Mathf.PI / 4f; + Vector3 p = OnMesh(at + new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle)) * dist); + NavMeshHit hit; + if (!SampleNavmesh(p, 1f, out hit)) continue; + // Each one's eyes to the other's centre, both ways, as RunicNPC's own line of sight runs. + if (Physics.Linecast(at + Vector3.up * 1.6f, p + Vector3.up * 0.9f, 1218519041, QueryTriggerInteraction.Ignore) || + Physics.Linecast(p + Vector3.up * 1.6f, at + Vector3.up * 0.9f, 1218519041, QueryTriggerInteraction.Ignore)) continue; + if (k > 0) Note($"s5: the partner {dist:0} m from {at} stands {k * 45}° round from north (no clear line north)"); + return p; + } + + Note($"s5: no clear line {dist:0} m round {at}; the partner stands north as before"); + return OnMesh(at + new Vector3(0, 0, dist)); + } + private int HorrorLeft() => BaseNetworkable.serverEntities.OfType().Count(c => !c.IsDestroyed && c.faction == BaseCombatEntity.Faction.Horror); private IEnumerator Stage5() @@ -1793,16 +1818,18 @@ namespace Oxide.Plugins Vector3 d = OnMesh(f + new Vector3(70, 0, 0)); Vector3 e = OnMesh(f + new Vector3(140, 0, 0)); + // Each fight's second NPC where the two can see each other (issue #20), found before anyone stands there. + Vector3 aTo = Partner(a, 15f), bTo = Partner(b, 15f), cTo = Partner(c, 15f), dTo = Partner(d, 25f), eTo = Partner(e, 15f); BasePlayer red = Spawn(a, "t_red"); - BasePlayer blue = Spawn(OnMesh(a + new Vector3(0, 0, 15)), "t_blue"); + BasePlayer blue = Spawn(aTo, "t_blue"); BasePlayer hunter = Spawn(b, "t_hunter"); - ScientistNPC prey = SpawnScientist(b + new Vector3(0, 0, 15)); + ScientistNPC prey = SpawnScientist(bTo); BasePlayer neutral = Spawn(c, "t_roam"); - ScientistNPC bystander = SpawnScientist(c + new Vector3(0, 0, 15)); + ScientistNPC bystander = SpawnScientist(cTo); BasePlayer leashed = Spawn(d, "t_leashed"); - ScientistNPC far = SpawnScientist(d + new Vector3(0, 0, 25)); + ScientistNPC far = SpawnScientist(dTo); BasePlayer victim = Spawn(e, "t_roam"); - BasePlayer bully = Spawn(OnMesh(e + new Vector3(0, 0, 15)), "t_bully"); + BasePlayer bully = Spawn(eTo, "t_bully"); var fighters = new Dictionary { { "red", red }, { "blue", blue }, { "hunter", hunter }, { "prey", prey }, { "neutral", neutral }, @@ -1823,7 +1850,16 @@ namespace Oxide.Plugins if (factionsAt < 0f && (Gone(red) || Gone(blue))) factionsAt = t; if (scientistAt < 0f && Gone(prey)) scientistAt = t; if (!Gone(neutral) && TargetOf(Describe(neutral)) != "none") neutralFought = true; - if (!Gone(leashed) && TargetOf(Describe(leashed)) != "none") leashedFought = true; + if (!leashedFought && !Gone(leashed) && TargetOf(Describe(leashed)) != "none") + { + leashedFought = true; + // Run 49 failed here once and it has not been seen since: say how it came to fight. A target its + // own sense found is within its chase (8 m) of home; a hurt one was shot; otherwise an ally's alarm. + Dictionary dl = Describe(leashed); + Vector3 leashHome = V(dl["home"]); + Note($"s5.fight.leash: fighting at {t} s, target {dl["target"]}, health {Convert.ToSingle(dl["health"]):0}/{Convert.ToSingle(dl["maxHealth"]):0}, " + + $"{Flat(V(dl["position"]), leashHome):0.0} m from home; the scientist {(Gone(far) ? "gone" : $"{Flat(far.transform.position, leashHome):0.0} m from its home, {far.health:0} hp")}"); + } if (!Gone(victim) && TargetOf(Describe(victim)) != "none") victimAnswered = true; foreach (BasePlayer x in new[] { red, blue, hunter }) {