@hytopia.com/examples 1.0.8 → 1.0.9
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.
- package/frontiers-rpg-game/assets/maps/weavers-hollow.json +5448 -5967
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/baseColor.png +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver-named-nodes.bin +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver-named-nodes.gltf +7586 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.bin +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.gltf +3838 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver/weaver.gltf.md5 +1 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/baseColor.png +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling-named-nodes.bin +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling-named-nodes.gltf +9726 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.bin +0 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.gltf +9478 -0
- package/frontiers-rpg-game/assets/models/enemies/.optimized/weaver-broodling/weaver-broodling.gltf.md5 +1 -0
- package/frontiers-rpg-game/assets/models/enemies/weaver-broodling.gltf +1 -0
- package/frontiers-rpg-game/assets/models/enemies/weaver.gltf +1 -0
- package/frontiers-rpg-game/src/GameClock.ts +1 -0
- package/frontiers-rpg-game/src/GameManager.ts +4 -5
- package/frontiers-rpg-game/src/GamePlayer.ts +18 -13
- package/frontiers-rpg-game/src/GamePlayerEntity.ts +8 -0
- package/frontiers-rpg-game/src/GameRegion.ts +28 -6
- package/frontiers-rpg-game/src/entities/BaseCombatEntity.ts +67 -22
- package/frontiers-rpg-game/src/entities/BaseEntity.ts +7 -2
- package/frontiers-rpg-game/src/entities/PortalEntity.ts +41 -13
- package/frontiers-rpg-game/src/entities/enemies/LesserBlightBloomEntity.ts +1 -2
- package/frontiers-rpg-game/src/entities/enemies/QueenWeaverEntity.ts +155 -0
- package/frontiers-rpg-game/src/entities/enemies/RatkinBruteEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/RatkinRangerEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/RatkinSpellcasterEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/RatkinWarriorEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinBruteEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinRangerEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinSpellcasterEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/TaintedRatkinWarriorEntity.ts +1 -1
- package/frontiers-rpg-game/src/entities/enemies/WeaverBroodlingEntity.ts +47 -0
- package/frontiers-rpg-game/src/entities/environmental/SpiderWebEntity.ts +34 -4
- package/frontiers-rpg-game/src/items/BaseWeaponItem.ts +1 -1
- package/frontiers-rpg-game/src/items/weapons/DullSwordItem.ts +0 -1
- package/frontiers-rpg-game/src/items/weapons/IronLongSwordItem.ts +0 -1
- package/frontiers-rpg-game/src/items/weapons/TrainingSwordItem.ts +0 -1
- package/frontiers-rpg-game/src/quests/main/WelcomeToStalkhavenQuest.ts +2 -1
- package/frontiers-rpg-game/src/regions/ratkin-nest/RatkinNestRegion.ts +31 -0
- package/frontiers-rpg-game/src/regions/stalkhaven-port/StalkhavenPortRegion.ts +1 -0
- package/frontiers-rpg-game/src/regions/weavers-hollow/WeaversHollowRegion.ts +67 -6
- package/frontiers-rpg-game/src/systems/Spawner.ts +2 -2
- 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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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,
|
|
1
|
+
import { Collider, CollisionGroup, Entity, ModelEntityOptions, RigidBodyType } from 'hytopia';
|
|
2
|
+
import GamePlayerEntity from '../../GamePlayerEntity';
|
|
2
3
|
|
|
3
4
|
export type SpiderWebEntityOptions = {
|
|
4
5
|
|
|
5
|
-
} &
|
|
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
|
-
...
|
|
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
|
|
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
|
}
|
|
@@ -81,7 +81,8 @@ export default class WelcomeToStalkhavenQuest extends BaseQuest {
|
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
enabledForInteractor: (interactor: GamePlayerEntity) => {
|
|
84
|
-
|
|
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
|
}
|
|
@@ -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.
|
|
19
|
+
maxDirectionalLightIntensity: 0.8,
|
|
18
20
|
minAmbientLightIntensity: 0.055,
|
|
19
|
-
minDirectionalLightIntensity: 0.
|
|
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
|
|
35
|
-
|
|
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
|
}
|
|
@@ -22,7 +22,7 @@ export type SpawnRegion = BoundingBox & {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export type EntitySpawnable = {
|
|
25
|
-
entityConstructor: new () =>
|
|
25
|
+
entityConstructor: new () => Entity;
|
|
26
26
|
weight: number;
|
|
27
27
|
wanders?: boolean;
|
|
28
28
|
wanderOptions?: WanderOptions;
|
|
@@ -139,7 +139,7 @@ export default class Spawner {
|
|
|
139
139
|
instance.spawn(this._world, spawnPoint, rotation);
|
|
140
140
|
|
|
141
141
|
// Apply wandering behavior if configured
|
|
142
|
-
if (spawnable.wanders && spawnable.wanderOptions) {
|
|
142
|
+
if (spawnable.wanders && spawnable.wanderOptions && instance instanceof BaseEntity) {
|
|
143
143
|
instance.wander(instance.moveSpeed, spawnable.wanderOptions);
|
|
144
144
|
}
|
|
145
145
|
|