@hytopia.com/examples 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/frontiers-rpg-game/assets/maps/weavers-hollow.json +5448 -5967
  2. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/baseColor.png +0 -0
  3. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver-named-nodes.bin +0 -0
  4. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver-named-nodes.gltf +7586 -0
  5. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.bin +0 -0
  6. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.gltf +3838 -0
  7. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.gltf.md5 +1 -0
  8. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/baseColor.png +0 -0
  9. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling-named-nodes.bin +0 -0
  10. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling-named-nodes.gltf +9726 -0
  11. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.bin +0 -0
  12. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.gltf +9478 -0
  13. package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.gltf.md5 +1 -0
  14. package/frontiers-rpg-game/assets/models/enemies/weaver-broodling.gltf +1 -0
  15. package/frontiers-rpg-game/assets/models/enemies/weaver.gltf +1 -0
  16. package/frontiers-rpg-game/src/GameClock.ts +1 -0
  17. package/frontiers-rpg-game/src/GameManager.ts +4 -5
  18. package/frontiers-rpg-game/src/GamePlayer.ts +18 -13
  19. package/frontiers-rpg-game/src/GamePlayerEntity.ts +8 -0
  20. package/frontiers-rpg-game/src/GameRegion.ts +28 -6
  21. package/frontiers-rpg-game/src/entities/BaseCombatEntity.ts +67 -22
  22. package/frontiers-rpg-game/src/entities/BaseEntity.ts +7 -2
  23. package/frontiers-rpg-game/src/entities/PortalEntity.ts +41 -13
  24. package/frontiers-rpg-game/src/entities/enemies/LesserBlightBloomEntity.ts +3 -4
  25. package/frontiers-rpg-game/src/entities/enemies/QueenWeaverEntity.ts +155 -0
  26. package/frontiers-rpg-game/src/entities/enemies/RatkinBruteEntity.ts +4 -4
  27. package/frontiers-rpg-game/src/entities/enemies/RatkinRangerEntity.ts +3 -3
  28. package/frontiers-rpg-game/src/entities/enemies/RatkinSpellcasterEntity.ts +1 -1
  29. package/frontiers-rpg-game/src/entities/enemies/RatkinWarriorEntity.ts +3 -3
  30. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinBruteEntity.ts +4 -4
  31. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinRangerEntity.ts +1 -1
  32. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinSpellcasterEntity.ts +1 -1
  33. package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinWarriorEntity.ts +3 -3
  34. package/frontiers-rpg-game/src/entities/enemies/WeaverBroodlingEntity.ts +47 -0
  35. package/frontiers-rpg-game/src/entities/environmental/SpiderWebEntity.ts +34 -4
  36. package/frontiers-rpg-game/src/items/BaseWeaponItem.ts +1 -1
  37. package/frontiers-rpg-game/src/items/weapons/DullSwordItem.ts +0 -1
  38. package/frontiers-rpg-game/src/items/weapons/IronLongSwordItem.ts +0 -1
  39. package/frontiers-rpg-game/src/items/weapons/TrainingSwordItem.ts +0 -1
  40. package/frontiers-rpg-game/src/quests/main/WelcomeToStalkhavenQuest.ts +2 -1
  41. package/frontiers-rpg-game/src/regions/ratkin-nest/RatkinNestRegion.ts +31 -0
  42. package/frontiers-rpg-game/src/regions/stalkhaven-port/StalkhavenPortRegion.ts +1 -0
  43. package/frontiers-rpg-game/src/regions/weavers-hollow/WeaversHollowRegion.ts +67 -6
  44. package/frontiers-rpg-game/src/systems/Spawner.ts +2 -2
  45. package/package.json +1 -1
@@ -0,0 +1,155 @@
1
+ import { Collider, ColliderShape, RigidBodyType } from 'hytopia';
2
+ import BaseCombatEntity, { BaseCombatEntityOptions } from '../BaseCombatEntity';
3
+ import GamePlayerEntity from '../../GamePlayerEntity';
4
+ import type BaseEntity from '../BaseEntity';
5
+
6
+ import WeaverBroodlingEntity from './WeaverBroodlingEntity';
7
+
8
+ // Drops
9
+ import GoldItem from "../../items/general/GoldItem";
10
+ import RareSeedsItem from '../../items/seeds/RareSeedsItem';
11
+ import UnusualSeedsItem from '../../items/seeds/UnusualSeedsItem';
12
+
13
+ export type QueenWeaverEntityOptions = {
14
+
15
+ } & Partial<BaseCombatEntityOptions>;
16
+
17
+ export default class QueenWeaverEntity extends BaseCombatEntity {
18
+ constructor(options?: QueenWeaverEntityOptions) {
19
+ super({
20
+ aggroRadius: 20,
21
+ attacks: [
22
+ {
23
+ animations: [ 'bite' ],
24
+ cooldownMs: 1500,
25
+ range: 2,
26
+ simpleAttackDamage: 50,
27
+ simpleAttackDamageVariance: 0.2,
28
+ simpleAttackDamageDelayMs: 400,
29
+ simpleAttackReach: 3,
30
+ weight: 10,
31
+ },
32
+ {
33
+ animations: [ 'cocoon' ],
34
+ complexAttack: (params) => this._pounce(params.target),
35
+ complexAttackDelayMs: 300,
36
+ cooldownMs: 2500,
37
+ range: 10,
38
+ weight: 10,
39
+ },
40
+ {
41
+ animations: [ 'web' ],
42
+ complexAttack: () => this._slam(),
43
+ complexAttackDelayMs: 600,
44
+ cooldownMs: 2500,
45
+ range: 5,
46
+ weight: 10,
47
+ },
48
+ {
49
+ animations: [ 'gas' ],
50
+ complexAttack: () => this._spawnBroodling(),
51
+ complexAttackDelayMs: 5000,
52
+ cooldownMs: 6500,
53
+ range: 8,
54
+ stopMovingDuringDelay: true,
55
+ weight: 4,
56
+ }
57
+ ],
58
+ combatExperienceReward: 650,
59
+ deathAnimations: [ 'death' ],
60
+ deathDespawnDelayMs: 1500,
61
+ deathItemDrops: [
62
+ { itemClass: GoldItem, minQuantity: 93, maxQuantity: 186, weight: 40 },
63
+ { itemClass: UnusualSeedsItem, minQuantity: 1, maxQuantity: 3, weight: 20 },
64
+ { itemClass: RareSeedsItem, minQuantity: 1, maxQuantity: 3, weight: 1 },
65
+ ],
66
+ deathItemMaxDrops: 5,
67
+ faceSpeed: 4,
68
+ health: 1000,
69
+ idleAnimations: [ 'waiting' ],
70
+ modelUri: 'models/enemies/weaver.gltf',
71
+ modelScale: 1.3,
72
+ moveAnimations: [ 'walking' ],
73
+ moveSpeed: 5,
74
+ name: 'Queen Weaver',
75
+ pathfindingOptions: {
76
+ maxJump: 3,
77
+ maxFall: 4,
78
+ maxOpenSetIterations: 50,
79
+ },
80
+ pushable: false,
81
+ rigidBodyOptions: {
82
+ type: RigidBodyType.DYNAMIC,
83
+ ccdEnabled: true,
84
+ },
85
+ ...options,
86
+ })
87
+ }
88
+
89
+ private _pounce(target: BaseEntity | GamePlayerEntity) {
90
+ if (!this.world) return;
91
+
92
+ const position = target.position;
93
+ const direction = this.calculateDirectionToTargetPosition(position);
94
+
95
+ this.applyImpulse({
96
+ x: direction.x * 10 * this.mass,
97
+ y: 5 * this.mass,
98
+ z: direction.z * 10 * this.mass,
99
+ });
100
+
101
+ this.faceTowards(position, 10);
102
+
103
+ setTimeout(() => {
104
+ if (!target.isSpawned || target.isDead) return;
105
+
106
+ if (this.calculateDistanceSquaredToTarget(target) < this.diameterSquared + 1) {
107
+ target.takeDamage(this.calculateDamageWithVariance(65, 0.4));
108
+ }
109
+ }, 1100);
110
+ }
111
+
112
+ private _slam() {
113
+ if (!this.world) return;
114
+
115
+ const aoeCollider = new Collider({
116
+ shape: ColliderShape.CYLINDER,
117
+ halfHeight: this.height / 2,
118
+ radius: Math.sqrt(this.diameterSquared) / 2 + 2,
119
+ })
120
+
121
+ const targets = this.getTargetsByRawShapeIntersection(
122
+ aoeCollider.rawShape,
123
+ this.position,
124
+ this.rotation,
125
+ );
126
+
127
+ for (const target of targets) {
128
+ if (!target.isSpawned || !(target instanceof GamePlayerEntity)) continue;
129
+
130
+ target.takeDamage(this.calculateDamageWithVariance(70, 0.2));
131
+
132
+ if (!target.isDodging) { // we check here to allow damage to resolve for dodged exp
133
+ const direction = this.calculateDirectionToTargetPosition(target.position);
134
+ target.applyImpulse({
135
+ x: direction.x * 13 * target.mass,
136
+ y: 5 * target.mass,
137
+ z: direction.z * 13 * target.mass,
138
+ });
139
+ }
140
+ }
141
+ }
142
+
143
+ private _spawnBroodling() {
144
+ if (!this.world) return;
145
+
146
+ const weaverBroodling = new WeaverBroodlingEntity();
147
+ const facingDirection = this.directionFromRotation;
148
+
149
+ weaverBroodling.spawn(this.world, {
150
+ x: this.position.x + (-facingDirection.x * 3),
151
+ y: this.position.y + 1,
152
+ z: this.position.z + (-facingDirection.z * 3),
153
+ });
154
+ }
155
+ }
@@ -11,7 +11,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
11
11
 
12
12
  export type RatkinBruteEntityOptions = {
13
13
 
14
- } & BaseCombatEntityOptions;
14
+ } & Partial<BaseCombatEntityOptions>;
15
15
 
16
16
  export default class RatkinBruteEntity extends BaseCombatEntity {
17
17
  constructor(options?: RatkinBruteEntityOptions) {
@@ -22,7 +22,7 @@ export default class RatkinBruteEntity extends BaseCombatEntity {
22
22
  { // Heavy body slam attack
23
23
  animations: [ 'atk1' ],
24
24
  cooldownMs: 4000,
25
- range: 2,
25
+ range: 1.5,
26
26
  simpleAttackDamage: 20,
27
27
  simpleAttackDamageVariance: 0.6, // ±60% damage (12-28)
28
28
  simpleAttackDamageDelayMs: 1000, // Deal damage 1000ms into animation
@@ -32,7 +32,7 @@ export default class RatkinBruteEntity extends BaseCombatEntity {
32
32
  { // Light hammer swing attack
33
33
  animations: [ 'atk2' ],
34
34
  cooldownMs: 1500,
35
- range: 2,
35
+ range: 1.5,
36
36
  simpleAttackDamage: 16,
37
37
  simpleAttackDamageVariance: 0.15, // ±15% damage (7-11)
38
38
  simpleAttackDamageDelayMs: 500, // Deal damage 500ms into animation
@@ -42,7 +42,7 @@ export default class RatkinBruteEntity extends BaseCombatEntity {
42
42
  { // Light hammer swing 2 attack
43
43
  animations: [ 'atk3' ],
44
44
  cooldownMs: 1500,
45
- range: 2,
45
+ range: 1.5,
46
46
  simpleAttackDamage: 13,
47
47
  simpleAttackDamageVariance: 0.15, // ±15% damage (5-9)
48
48
  simpleAttackDamageDelayMs: 350, // Deal damage 350ms into animation
@@ -13,7 +13,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
13
13
 
14
14
  export type RatkinRangerEntityOptions = {
15
15
 
16
- } & BaseCombatEntityOptions;
16
+ } & Partial<BaseCombatEntityOptions>;
17
17
 
18
18
  export default class RatkinRangerEntity extends BaseCombatEntity {
19
19
  constructor(options?: RatkinRangerEntityOptions) {
@@ -28,7 +28,7 @@ export default class RatkinRangerEntity extends BaseCombatEntity {
28
28
  },
29
29
  complexAttackDelayMs: 700,
30
30
  cooldownMs: 2000,
31
- range: 10,
31
+ range: 9,
32
32
  weight: 2,
33
33
  },
34
34
  { // Slow heavy bow attack
@@ -38,7 +38,7 @@ export default class RatkinRangerEntity extends BaseCombatEntity {
38
38
  },
39
39
  complexAttackDelayMs: 1600,
40
40
  cooldownMs: 3000,
41
- range: 10,
41
+ range: 9,
42
42
  weight: 3,
43
43
  },
44
44
  ],
@@ -13,7 +13,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
13
13
 
14
14
  export type RatkinSpellcasterEntityOptions = {
15
15
 
16
- } & BaseCombatEntityOptions;
16
+ } & Partial<BaseCombatEntityOptions>;
17
17
 
18
18
  export default class RatkinSpellcasterEntity extends BaseCombatEntity {
19
19
  constructor(options?: RatkinSpellcasterEntityOptions) {
@@ -11,7 +11,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
11
11
 
12
12
  export type RatkinWarriorEntityOptions = {
13
13
 
14
- } & BaseCombatEntityOptions;
14
+ } & Partial<BaseCombatEntityOptions>;
15
15
 
16
16
  export default class RatkinWarriorEntity extends BaseCombatEntity {
17
17
  constructor(options?: RatkinWarriorEntityOptions) {
@@ -22,7 +22,7 @@ export default class RatkinWarriorEntity extends BaseCombatEntity {
22
22
  { // Heavy attack
23
23
  animations: [ 'atk1' ],
24
24
  cooldownMs: 4000,
25
- range: 2,
25
+ range: 1.5,
26
26
  simpleAttackDamage: 16,
27
27
  simpleAttackDamageVariance: 0.4, // ±40% damage (12-20)
28
28
  simpleAttackDamageDelayMs: 1000, // Deal damage 1000ms into animation
@@ -32,7 +32,7 @@ export default class RatkinWarriorEntity extends BaseCombatEntity {
32
32
  { // Light attack
33
33
  animations: [ 'atk2' ],
34
34
  cooldownMs: 2000,
35
- range: 2,
35
+ range: 1.5,
36
36
  simpleAttackDamage: 9,
37
37
  simpleAttackDamageVariance: 0.1, // ±10% damage (6-8)
38
38
  simpleAttackDamageDelayMs: 400, // Deal damage 400ms into animation
@@ -11,7 +11,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
11
11
 
12
12
  export type TaintedRatkinBruteEntityOptions = {
13
13
 
14
- } & BaseCombatEntityOptions;
14
+ } & Partial<BaseCombatEntityOptions>;
15
15
 
16
16
  export default class TaintedRatkinBruteEntity extends BaseCombatEntity {
17
17
  constructor(options?: TaintedRatkinBruteEntityOptions) {
@@ -22,7 +22,7 @@ export default class TaintedRatkinBruteEntity extends BaseCombatEntity {
22
22
  { // Heavy body slam attack
23
23
  animations: [ 'atk1' ],
24
24
  cooldownMs: 3500,
25
- range: 2,
25
+ range: 1.5,
26
26
  simpleAttackDamage: 30,
27
27
  simpleAttackDamageVariance: 0.6, // ±60% damage
28
28
  simpleAttackDamageDelayMs: 1000, // Deal damage 1000ms into animation
@@ -32,7 +32,7 @@ export default class TaintedRatkinBruteEntity extends BaseCombatEntity {
32
32
  { // Light hammer swing attack
33
33
  animations: [ 'atk2' ],
34
34
  cooldownMs: 1000,
35
- range: 2,
35
+ range: 1.5,
36
36
  simpleAttackDamage: 18,
37
37
  simpleAttackDamageVariance: 0.15, // ±15% damage
38
38
  simpleAttackDamageDelayMs: 500, // Deal damage 500ms into animation
@@ -42,7 +42,7 @@ export default class TaintedRatkinBruteEntity extends BaseCombatEntity {
42
42
  { // Light hammer swing 2 attack
43
43
  animations: [ 'atk3' ],
44
44
  cooldownMs: 1000,
45
- range: 2,
45
+ range: 1.5,
46
46
  simpleAttackDamage: 13,
47
47
  simpleAttackDamageVariance: 0.15, // ±15% damage
48
48
  simpleAttackDamageDelayMs: 350, // Deal damage 350ms into animation
@@ -14,7 +14,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
14
14
 
15
15
  export type TaintedRatkinRangerEntityOptions = {
16
16
 
17
- } & BaseCombatEntityOptions;
17
+ } & Partial<BaseCombatEntityOptions>;
18
18
 
19
19
  export default class TaintedRatkinRangerEntity extends BaseCombatEntity {
20
20
  constructor(options?: TaintedRatkinRangerEntityOptions) {
@@ -13,7 +13,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
13
13
 
14
14
  export type TaintedRatkinSpellcasterEntityOptions = {
15
15
 
16
- } & BaseCombatEntityOptions;
16
+ } & Partial<BaseCombatEntityOptions>;
17
17
 
18
18
  export default class TaintedRatkinSpellcasterEntity extends BaseCombatEntity {
19
19
  constructor(options?: TaintedRatkinSpellcasterEntityOptions) {
@@ -11,7 +11,7 @@ import RatkinToothItem from "../../items/materials/RatkinToothItem.ts";
11
11
 
12
12
  export type TaintedRatkinWarriorEntityOptions = {
13
13
 
14
- } & BaseCombatEntityOptions;
14
+ } & Partial<BaseCombatEntityOptions>;
15
15
 
16
16
  export default class TaintedRatkinWarriorEntity extends BaseCombatEntity {
17
17
  constructor(options?: TaintedRatkinWarriorEntityOptions) {
@@ -22,7 +22,7 @@ export default class TaintedRatkinWarriorEntity extends BaseCombatEntity {
22
22
  { // Heavy attack
23
23
  animations: [ 'atk1' ],
24
24
  cooldownMs: 3500,
25
- range: 2,
25
+ range: 1.5,
26
26
  simpleAttackDamage: 35,
27
27
  simpleAttackDamageVariance: 0.4, // ±40% damage (15-35)
28
28
  simpleAttackDamageDelayMs: 1000, // Deal damage 1000ms into animation
@@ -32,7 +32,7 @@ export default class TaintedRatkinWarriorEntity extends BaseCombatEntity {
32
32
  { // Light attack
33
33
  animations: [ 'atk2' ],
34
34
  cooldownMs: 1500,
35
- range: 2,
35
+ range: 1.5,
36
36
  simpleAttackDamage: 15,
37
37
  simpleAttackDamageVariance: 0.1, // ±10% damage (9-11)
38
38
  simpleAttackDamageDelayMs: 400, // Deal damage 400ms into animation
@@ -0,0 +1,47 @@
1
+ import BaseCombatEntity, { BaseCombatEntityOptions } from "../BaseCombatEntity";
2
+
3
+ // Drops
4
+ import GoldItem from "../../items/general/GoldItem";
5
+
6
+ export type WeaverBroodlingEntityOptions = {
7
+
8
+ } & Partial<BaseCombatEntityOptions>;
9
+
10
+ export default class WeaverBroodlingEntity extends BaseCombatEntity {
11
+ constructor(options?: WeaverBroodlingEntityOptions) {
12
+ super({
13
+ aggroRadius: 12,
14
+ aggroSensorForwardOffset: 0,
15
+ attacks: [
16
+ { // Bite attack
17
+ animations: [ 'fang_attack' ],
18
+ cooldownMs: 2000,
19
+ range: 2,
20
+ simpleAttackDamage: 17,
21
+ simpleAttackDamageVariance: 0.1,
22
+ simpleAttackDamageDelayMs: 600,
23
+ weight: 1,
24
+ }
25
+ ],
26
+ combatExperienceReward: 30,
27
+ deathAnimations: [ 'death' ],
28
+ deathDespawnDelayMs: 1000,
29
+ deathItemDrops: [
30
+ { itemClass: GoldItem, minQuantity: 16, maxQuantity: 24, weight: 2 },
31
+ ],
32
+ health: 140,
33
+ idleAnimations: [ 'idle' ],
34
+ modelUri: 'models/enemies/weaver-broodling.gltf',
35
+ modelScale: 0.8,
36
+ moveAnimations: [ 'walk' ],
37
+ moveSpeed: 3,
38
+ name: 'Weaver Broodling',
39
+ pathfindingOptions: {
40
+ maxJump: 3,
41
+ maxFall: 3,
42
+ },
43
+ tintColor: { r: 64, g: 255, b: 64 },
44
+ ...options,
45
+ });
46
+ }
47
+ }
@@ -1,20 +1,50 @@
1
- import { Collider, Entity, EntityOptions, RigidBodyType } from 'hytopia';
1
+ import { Collider, CollisionGroup, Entity, ModelEntityOptions, RigidBodyType } from 'hytopia';
2
+ import GamePlayerEntity from '../../GamePlayerEntity';
2
3
 
3
4
  export type SpiderWebEntityOptions = {
4
5
 
5
- } & EntityOptions;
6
+ } & ModelEntityOptions;
6
7
 
7
8
  export default class SpiderWebEntity extends Entity {
8
9
  public constructor(options?: SpiderWebEntityOptions) {
10
+ const modelScale = options?.modelScale ?? Math.random() * 1.5 + 0.5; // 0.5 to 2 scale
11
+ const colliderOptions = Collider.optionsFromModelUri('models/vfx/spider-web.gltf', modelScale);
12
+
9
13
  super({
10
14
  ...options,
11
15
  modelUri: 'models/vfx/spider-web.gltf',
16
+ modelScale,
12
17
  rigidBodyOptions: {
13
18
  type: RigidBodyType.DYNAMIC,
14
19
  colliders: [
15
- {
16
- ...Collider.optionsFromModelUri('models/vfx/spider-web.gltf'),
20
+ { // Collider for ground contact
21
+ ...colliderOptions,
22
+ bounciness: 0,
23
+ collisionGroups: {
24
+ belongsTo: [ CollisionGroup.ENTITY ],
25
+ collidesWith: [ CollisionGroup.BLOCK ],
26
+ }
27
+ },
28
+ { // Collider for slow effect on relevant entities
29
+ ...colliderOptions,
30
+ collisionGroups: {
31
+ belongsTo: [ CollisionGroup.ENTITY ],
32
+ collidesWith: [ CollisionGroup.ENTITY ],
33
+ },
17
34
  isSensor: true,
35
+ onCollision: (entity, started) => {
36
+ if (!(entity instanceof GamePlayerEntity)) return;
37
+
38
+ if (started) {
39
+ entity.setGravityScale(2);
40
+ entity.playerController.walkVelocity = entity.playerController.walkVelocity * 0.5;
41
+ entity.playerController.runVelocity = entity.playerController.runVelocity * 0.5;
42
+ } else {
43
+ entity.setGravityScale(1);
44
+ entity.playerController.walkVelocity = entity.playerController.walkVelocity * 2;
45
+ entity.playerController.runVelocity = entity.playerController.runVelocity * 2;
46
+ }
47
+ }
18
48
  }
19
49
  ]
20
50
  }
@@ -146,7 +146,7 @@ export default abstract class BaseWeaponItem extends BaseItem {
146
146
  }
147
147
  }
148
148
 
149
- protected getTargetsByRawShapeIntersection(rawShape: RawShape, position: Vector3Like, rotation: QuaternionLike, reach: number): Entity[] {
149
+ protected getTargetsByRawShapeIntersection(rawShape: RawShape, position: Vector3Like, rotation: QuaternionLike): Entity[] {
150
150
  if (!this.entity?.parent || !this.entity.parent.world) {
151
151
  return [];
152
152
  }
@@ -49,7 +49,6 @@ export default class DullSwordItem extends BaseWeaponItem {
49
49
  spinCollider.rawShape,
50
50
  this.entity.parent.position,
51
51
  this.entity.parent.rotation,
52
- attack.reach,
53
52
  );
54
53
 
55
54
  for (const target of targets) {
@@ -49,7 +49,6 @@ export default class IronLongSwordItem extends BaseWeaponItem {
49
49
  spinCollider.rawShape,
50
50
  this.entity.parent.position,
51
51
  this.entity.parent.rotation,
52
- attack.reach,
53
52
  );
54
53
 
55
54
  for (const target of targets) {
@@ -49,7 +49,6 @@ export default class TrainingSwordItem extends BaseWeaponItem {
49
49
  spinCollider.rawShape,
50
50
  this.entity.parent.position,
51
51
  this.entity.parent.rotation,
52
- attack.reach,
53
52
  );
54
53
 
55
54
  for (const target of targets) {
@@ -81,7 +81,8 @@ export default class WelcomeToStalkhavenQuest extends BaseQuest {
81
81
  },
82
82
  },
83
83
  enabledForInteractor: (interactor: GamePlayerEntity) => {
84
- return interactor.gamePlayer.questLog.isQuestActive(this.id);
84
+ // This || may be unecessary but a bug was found in testing with persistence that broke quest state..
85
+ return interactor.gamePlayer.questLog.isQuestActive(this.id) || !interactor.gamePlayer.questLog.hasQuest(ExploringStalkhavenQuest.id);
85
86
  }
86
87
  }
87
88
  ];
@@ -1,3 +1,4 @@
1
+ import { Quaternion } from 'hytopia';
1
2
  import GameRegion from '../../GameRegion';
2
3
  import Spawner from '../../systems/Spawner';
3
4
  import PortalEntity from '../../entities/PortalEntity';
@@ -19,6 +20,8 @@ import TaintedRatkinWarriorEntity from '../../entities/enemies/TaintedRatkinWarr
19
20
 
20
21
  import LesserBlightBloomEntity from '../../entities/enemies/LesserBlightBloomEntity';
21
22
 
23
+ import WeaverBroodlingEntity from '../../entities/enemies/WeaverBroodlingEntity';
24
+
22
25
  // Spawner Forageables
23
26
  import DecayingPileEntity from '../../entities/forageables/DecayingPileEntity';
24
27
 
@@ -48,6 +51,7 @@ export default class RatkinNestRegion extends GameRegion {
48
51
  this._setupNPCs();
49
52
  this._setupPortals();
50
53
  }
54
+
51
55
  private _setupEnemySpawners(): void {
52
56
  const wanderOptions: WanderOptions = {
53
57
  idleMinMs: 8000,
@@ -124,11 +128,29 @@ export default class RatkinNestRegion extends GameRegion {
124
128
  ],
125
129
  spawnIntervalMs: 60000,
126
130
  world: this.world,
131
+ });
132
+
133
+ const weaverBroodlingSpawner = new Spawner({
134
+ groundCheckDistance: 4,
135
+ maxSpawns: 6,
136
+ spawnables: [
137
+ { entityConstructor: WeaverBroodlingEntity, weight: 1 },
138
+ ],
139
+ spawnRegions: [
140
+ {
141
+ min: { x: -4, y: 2, z: -112 },
142
+ max: { x: 45, y: 5, z: -64 },
143
+ weight: 1,
144
+ }
145
+ ],
146
+ spawnIntervalMs: 15000,
147
+ world: this.world,
127
148
  })
128
149
 
129
150
  upperNestSpawner.start(true);
130
151
  lowerNestSpawner.start(true);
131
152
  lowerNestLesserBlightBloomSpawner.start(true);
153
+ weaverBroodlingSpawner.start(true);
132
154
  }
133
155
 
134
156
  private _setupForageableSpawners(): void {
@@ -176,5 +198,14 @@ export default class RatkinNestRegion extends GameRegion {
176
198
  });
177
199
 
178
200
  chitterForestPortal.spawn(this.world, { x: -31, y: 23.5, z: -71 });
201
+
202
+ const weaversHollowPortal = new PortalEntity({
203
+ destinationRegionId: 'weavers-hollow',
204
+ destinationRegionPosition: { x: 10, y: 2, z: 13 },
205
+ destinationRegionFacingAngle: 45,
206
+ type: 'boss',
207
+ });
208
+
209
+ weaversHollowPortal.spawn(this.world, { x: 31, y: 3.5, z: -123 }, Quaternion.fromEuler(0, 45, 0));
179
210
  }
180
211
  }
@@ -37,6 +37,7 @@ export default class StalkhavenRegion extends GameRegion {
37
37
  destinationRegionFacingAngle: 90,
38
38
  modelScale: 2,
39
39
  });
40
+
40
41
  stalkhavenPortal.spawn(this.world, { x: -6, y: 9.5, z: -31 });
41
42
  }
42
43
  }
@@ -1,11 +1,13 @@
1
+ import { Quaternion } from 'hytopia';
1
2
  import GameRegion from '../../GameRegion';
2
3
  import Spawner from '../../systems/Spawner';
3
4
  import PortalEntity from '../../entities/PortalEntity';
4
- import type { WanderOptions } from '../../entities/BaseEntity';
5
5
 
6
6
  import weaversHollowMap from '../../../assets/maps/weavers-hollow.json';
7
7
 
8
8
  import SpiderWebEntity from '../../entities/environmental/SpiderWebEntity';
9
+ import QueenWeaverEntity from '../../entities/enemies/QueenWeaverEntity';
10
+ import WeaverBroodlingEntity from '../../entities/enemies/WeaverBroodlingEntity';
9
11
 
10
12
  export default class WeaversHollowRegion extends GameRegion {
11
13
  public constructor() {
@@ -14,9 +16,14 @@ export default class WeaversHollowRegion extends GameRegion {
14
16
  name: `Weaver's Hollow`,
15
17
  map: weaversHollowMap,
16
18
  maxAmbientLightIntensity: 0.075,
17
- maxDirectionalLightIntensity: 0.4,
19
+ maxDirectionalLightIntensity: 0.8,
18
20
  minAmbientLightIntensity: 0.055,
19
- minDirectionalLightIntensity: 0.2,
21
+ minDirectionalLightIntensity: 0.4,
22
+ respawnOverride: {
23
+ regionId: 'ratkin-nest',
24
+ facingAngle: 135,
25
+ spawnPoint: { x: -1, y: 10, z: -67 },
26
+ },
20
27
  skyboxUri: 'skyboxes/black',
21
28
  spawnPoint: { x: 10, y: 2, z: 13 },
22
29
  ambientAudioUri: 'audio/music/cave-theme-looping.mp3',
@@ -27,15 +34,69 @@ export default class WeaversHollowRegion extends GameRegion {
27
34
  super.setup();
28
35
 
29
36
  this._setupEnemySpawners();
37
+ this._setupEnvironmentSpawners();
30
38
  this._setupPortals();
31
39
  }
32
40
 
33
41
  private _setupEnemySpawners(): void {
34
- const spiderWeb = new SpiderWebEntity();
35
- spiderWeb.spawn(this.world, { x: 6, y: 5, z: 8 });
42
+ const queenWeaverSpawner = new Spawner({
43
+ maxSpawns: 1,
44
+ spawnables: [
45
+ { entityConstructor: QueenWeaverEntity, weight: 1 },
46
+ ],
47
+ spawnRegions: [
48
+ {
49
+ min: { x: -1, y: 16, z: 6 },
50
+ max: { x: 0, y: 16, z: 7 },
51
+ weight: 1,
52
+ }
53
+ ],
54
+ spawnIntervalMs: 120000,
55
+ world: this.world,
56
+ });
57
+
58
+ queenWeaverSpawner.start(true);
59
+
60
+ // Spawn lure broodlings
61
+ for (let i = 0; i < 3; i++) {
62
+ const weaverBroodling = new WeaverBroodlingEntity({ facingAngle: Math.random() * 360 });
63
+ weaverBroodling.spawn(this.world, {
64
+ x: Math.random() * 8 - 4,
65
+ y: 2,
66
+ z: Math.random() * 8 - 4,
67
+ });
68
+ }
69
+ }
70
+
71
+ private _setupEnvironmentSpawners(): void {
72
+ const spiderWebSpawner = new Spawner({
73
+ groundCheckDistance: 8,
74
+ maxSpawns: 20,
75
+ spawnables: [
76
+ { entityConstructor: SpiderWebEntity, weight: 1 },
77
+ ],
78
+ spawnRegions: [
79
+ {
80
+ min: { x: -13, y: 5, z: -9 },
81
+ max: { x: 13, y: 8, z: 11 },
82
+ weight: 1,
83
+ }
84
+ ],
85
+ spawnIntervalMs: 10000,
86
+ world: this.world,
87
+ });
88
+
89
+ spiderWebSpawner.start(true);
36
90
  }
37
91
 
38
92
  private _setupPortals(): void {
39
-
93
+ const ratkinNestPortal = new PortalEntity({
94
+ delayS: 10,
95
+ destinationRegionId: 'ratkin-nest',
96
+ destinationRegionPosition: { x: 35, y: 2, z: -118 },
97
+ destinationRegionFacingAngle: 240,
98
+ });
99
+
100
+ ratkinNestPortal.spawn(this.world, { x: 13.5, y: 3.5, z: 18 }, Quaternion.fromEuler(0, 45, 0));
40
101
  }
41
102
  }