-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CoC] Integrating features/APIs from Call Of Chornobyl merge into Ope…
…nXRay (#108) Signed-off-by: Neloreck <[email protected]>
- Loading branch information
Showing
36 changed files
with
709 additions
and
121 deletions.
There are no files selected for viewing
Submodule bin
updated
60 files
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/engine/core/ai/combat/combat_visibility_calculation.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { beforeEach, describe, expect, it } from "@jest/globals"; | ||
|
||
import { calculateObjectVisibility } from "@/engine/core/ai/combat/combat_visibility_calculation"; | ||
import { weatherConfig } from "@/engine/core/managers/weather/WeatherConfig"; | ||
import { MockGameObject } from "@/fixtures/xray"; | ||
|
||
describe("calculateObjectVisibility", () => { | ||
beforeEach(() => { | ||
weatherConfig.IS_UNDERGROUND_WEATHER = false; | ||
}); | ||
|
||
it("should correctly calculate outside values with negative distance / luminosity", () => { | ||
expect(calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 100, 10, -1, 5, 7, -1, 5, 10)).toBe( | ||
-1799.9964000000002 | ||
); | ||
expect( | ||
calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 100, 10, 0.00001, 5, 7, 0.00001, 5, 10) | ||
).toBe(-1799.9964000000002); | ||
}); | ||
|
||
it("should correctly calculate outside and inside", () => { | ||
weatherConfig.IS_UNDERGROUND_WEATHER = false; | ||
expect( | ||
calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 100, 10, 1, 5, 7, 150, 100, 10) | ||
).toBe(120); | ||
expect( | ||
calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 25.5, 5.5, 10.5, 8.5, 4.5, 2.5, 1.5, 0.5) | ||
).toBe(764.3045454545455); | ||
|
||
weatherConfig.IS_UNDERGROUND_WEATHER = true; | ||
expect( | ||
calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 100, 10, 1, 5, 7, 150, 100, 10) | ||
).toBe(42); | ||
expect( | ||
calculateObjectVisibility(MockGameObject.mock(), MockGameObject.mock(), 25.5, 5.5, 10.5, 8.5, 4.5, 2.5, 1.5, 0.5) | ||
).toBe(267.50659090909096); | ||
}); | ||
}); |
46 changes: 46 additions & 0 deletions
46
src/engine/core/ai/combat/combat_visibility_calculation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { weatherConfig } from "@/engine/core/managers/weather/WeatherConfig"; | ||
import { GameObject, Optional, TDistance, TDuration, TRate } from "@/engine/lib/types"; | ||
|
||
/** | ||
* If value >= visiblity_threshold then object is considered visible. | ||
* `visibility_threshold` is configured in LTX files for each monster / stalker separately. | ||
* | ||
* @param object - target object checking visibility | ||
* @param target - target checking visibility for from perspective of object | ||
* @param timeDelta - ? | ||
* @param timeQuantity - ? | ||
* @param luminosity - level of brightness outside | ||
* @param velocityFactor - ? | ||
* @param velocity - ? | ||
* @param distance - ? | ||
* @param objectDistance - ? | ||
* @param alwaysVisibleDistance -? | ||
* @returns visibility rate | ||
*/ | ||
export function calculateObjectVisibility( | ||
object: Optional<GameObject>, | ||
target: Optional<GameObject>, | ||
timeDelta: TDuration, | ||
timeQuantity: TDuration, | ||
luminosity: TRate, | ||
velocityFactor: TRate, | ||
velocity: TRate, | ||
distance: TDistance, | ||
objectDistance: TDistance, | ||
alwaysVisibleDistance: TDistance | ||
): TRate { | ||
luminosity = luminosity <= 0 ? 0.00001 : luminosity; | ||
distance = distance <= 0 ? 0.00001 : distance; | ||
|
||
if (weatherConfig.IS_UNDERGROUND_WEATHER) { | ||
luminosity *= 0.35; | ||
} | ||
|
||
// Unaltered formula from engine: | ||
// time_delta / time_quant * luminocity * (1 + velocity_factor*velocity) * (distance - object_distance) / | ||
// (distance - always_visible_distance) | ||
|
||
return ( | ||
((timeDelta / timeQuantity) * luminosity * (1 + velocityFactor * velocity) * (distance - objectDistance)) / distance | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { beforeEach, describe, expect, it } from "@jest/globals"; | ||
import { clsid } from "xray16"; | ||
|
||
import { selectBestStalkerWeapon } from "@/engine/core/ai/combat/combat_weapon_select"; | ||
import { getManager, registerSimulator } from "@/engine/core/database"; | ||
import { EGameEvent, EventsManager } from "@/engine/core/managers/events"; | ||
import { AnyObject, GameObject } from "@/engine/lib/types"; | ||
import { resetRegistry } from "@/fixtures/engine"; | ||
import { MockAlifeObject, MockGameObject } from "@/fixtures/xray"; | ||
|
||
describe("selectBestStalkerWeapon util", () => { | ||
beforeEach(() => { | ||
resetRegistry(); | ||
registerSimulator(); | ||
}); | ||
|
||
it("should fallback to null if no handlers found", () => { | ||
expect(selectBestStalkerWeapon(MockGameObject.mock(), MockGameObject.mock())).toBeNull(); | ||
expect(selectBestStalkerWeapon(MockGameObject.mock(), null)).toBeNull(); | ||
}); | ||
|
||
it("should handle exceptional cases from callback handlers without throwing", () => { | ||
const eventsManager: EventsManager = getManager(EventsManager); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
data.weaponId = true; | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(MockGameObject.mock(), MockGameObject.mock())).toBeNull(); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
data.weaponId = "test-string"; | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(MockGameObject.mock(), MockGameObject.mock())).toBeNull(); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
data.weaponId = {}; | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(MockGameObject.mock(), MockGameObject.mock())).toBeNull(); | ||
}); | ||
|
||
it("should use weapon from latest event handler", () => { | ||
const object: GameObject = MockGameObject.mock(); | ||
const weapon: GameObject = MockGameObject.mock(); | ||
|
||
const firstBestWeapon: GameObject = MockGameObject.mock(); | ||
const secondBestWeapon: GameObject = MockGameObject.mock(); | ||
|
||
MockAlifeObject.mock({ | ||
id: firstBestWeapon.id(), | ||
parentId: object.id(), | ||
clsid: clsid.wpn_svd, | ||
}); | ||
|
||
MockAlifeObject.mock({ | ||
id: secondBestWeapon.id(), | ||
parentId: object.id(), | ||
clsid: clsid.wpn_ak74, | ||
}); | ||
|
||
const eventsManager: EventsManager = getManager(EventsManager); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
expect(data).toEqual({ weaponId: null }); | ||
data.weaponId = firstBestWeapon.id(); | ||
} | ||
); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
expect(data).toEqual({ weaponId: firstBestWeapon.id() }); | ||
data.weaponId = secondBestWeapon.id(); | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(object, weapon)).toBe(secondBestWeapon); | ||
}); | ||
|
||
it("should require weapon ownership to use it", () => { | ||
const object: GameObject = MockGameObject.mock(); | ||
const weapon: GameObject = MockGameObject.mock(); | ||
|
||
const bestWeapon: GameObject = MockGameObject.mock(); | ||
|
||
MockAlifeObject.mock({ | ||
id: bestWeapon.id(), | ||
clsid: clsid.wpn_svd, | ||
}); | ||
|
||
const eventsManager: EventsManager = getManager(EventsManager); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
data.weaponId = bestWeapon.id(); | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(object, weapon)).toBeNull(); | ||
}); | ||
|
||
it("should require weapon cls id to use it", () => { | ||
const object: GameObject = MockGameObject.mock(); | ||
const weapon: GameObject = MockGameObject.mock(); | ||
|
||
const bestWeapon: GameObject = MockGameObject.mock(); | ||
|
||
MockAlifeObject.mock({ | ||
id: bestWeapon.id(), | ||
parentId: object.id(), | ||
clsid: clsid.obj_food, | ||
}); | ||
|
||
const eventsManager: EventsManager = getManager(EventsManager); | ||
|
||
eventsManager.registerCallback( | ||
EGameEvent.STALKER_WEAPON_SELECT, | ||
(_: GameObject, __: GameObject, data: AnyObject) => { | ||
data.weaponId = bestWeapon.id(); | ||
} | ||
); | ||
|
||
expect(selectBestStalkerWeapon(object, weapon)).toBeNull(); | ||
}); | ||
}); |
Oops, something went wrong.