Skip to content
This repository was archived by the owner on Feb 14, 2022. It is now read-only.

Commit 32a93ce

Browse files
committed
Finished part15
1 parent 15fc1bc commit 32a93ce

File tree

10 files changed

+364
-113
lines changed

10 files changed

+364
-113
lines changed

assets/dynamicglyph.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ class DynamicGlyph extends Glyph {
99
this._attachedMixins = {};
1010
// Create a similar object for groups
1111
this._attachedMixinGroups = {};
12+
// Set up an object for listeners
13+
this._listeners = {};
1214
// Setup the object's mixins
1315
var mixins = properties['mixins'] || [];
1416
for (var i = 0; i < mixins.length; i++) {
1517
// Copy over all properties from each mixin as long
16-
// as it's not the name or the init property. We
18+
// as it's not the name, init, or listeners property. We
1719
// also make sure not to override a property that
1820
// already exists on the entity.
1921
for (var key in mixins[i]) {
20-
if (key != 'init' && key != 'name' && !this.hasOwnProperty(key)) {
22+
if (key != 'init' && key != 'name' && key != 'listeners'
23+
&& !this.hasOwnProperty(key)) {
2124
this[key] = mixins[i][key];
2225
}
2326
}
@@ -27,6 +30,18 @@ class DynamicGlyph extends Glyph {
2730
if (mixins[i].groupName) {
2831
this._attachedMixinGroups[mixins[i].groupName] = true;
2932
}
33+
// Add all of our listeners
34+
if (mixins[i].listeners) {
35+
for (var key in mixins[i].listeners) {
36+
// If we don't already have a key for this event in our listeners
37+
// array, add it.
38+
if (!this._listeners[key]) {
39+
this._listeners[key] = [];
40+
}
41+
// Add the listener.
42+
this._listeners[key].push(mixins[i].listeners[key]);
43+
}
44+
}
3045
// Finally call the init function if there is one
3146
if (mixins[i].init) {
3247
mixins[i].init.call(this, properties);
@@ -69,4 +84,17 @@ class DynamicGlyph extends Glyph {
6984
return prefix + ' ' + this.describe();
7085
}
7186

87+
raiseEvent(event) {
88+
// Make sure we have at least one listener, or else exit
89+
if (!this._listeners[event]) {
90+
return;
91+
}
92+
// Extract any arguments passed, removing the event name
93+
var args = Array.prototype.slice.call(arguments, 1)
94+
// Invoke each listener, with this entity as the context and the arguments
95+
for (var i = 0; i < this._listeners[event].length; i++) {
96+
this._listeners[event][i].apply(this, args);
97+
}
98+
}
99+
72100
}

assets/entities.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,42 @@ Game.EntityRepository.define('kobold', {
9191
]
9292
});
9393

94+
Game.EntityRepository.define('giant zombie', {
95+
name: 'giant zombie',
96+
character: 'Z',
97+
foreground: 'teal',
98+
maxHp: 30,
99+
attackValue: 8,
100+
defenseValue: 5,
101+
level: 5,
102+
sightRadius: 6,
103+
mixins: [
104+
Game.EntityMixins.GiantZombieActor,
105+
Game.EntityMixins.Sight,
106+
Game.EntityMixins.Attacker,
107+
Game.EntityMixins.Destructible,
108+
Game.EntityMixins.CorpseDropper,
109+
Game.EntityMixins.ExperienceGainer
110+
]
111+
}, {
112+
disableRandomCreation: true
113+
});
114+
115+
Game.EntityRepository.define('slime', {
116+
name: 'slime',
117+
character: 's',
118+
foreground: 'lightGreen',
119+
maxHp: 10,
120+
attackValue: 5,
121+
sightRadius: 3,
122+
tasks: ['hunt', 'wander'],
123+
mixins: [
124+
Game.EntityMixins.TaskActor,
125+
Game.EntityMixins.Sight,
126+
Game.EntityMixins.Attacker,
127+
Game.EntityMixins.Destructible,
128+
Game.EntityMixins.CorpseDropper,
129+
Game.EntityMixins.ExperienceGainer,
130+
Game.EntityMixins.RandomStatGainer
131+
]
132+
});

assets/entity.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ class Entity extends DynamicGlyph {
106106
this.setPosition(x, y, z);
107107
}
108108
} else if (z > this.getZ()) {
109-
if (tile != Tile.stairsDownTile) {
109+
if (tile === Tile.holeToCavernTile &&
110+
this.hasMixin(Game.EntityMixins.PlayerActor)) {
111+
// Switch the entity to a boss cavern!
112+
this.switchMap(new Map.BossCavern());
113+
} else if (tile != Tile.stairsDownTile) {
110114
Game.sendMessage(this, "You can't go down here!");
111115
} else {
112116
this.setPosition(x, y, z);
@@ -174,4 +178,18 @@ class Entity extends DynamicGlyph {
174178
this.getMap().removeEntity(this);
175179
}
176180
}
181+
182+
switchMap(newMap) {
183+
// If it's the same map, nothing to do!
184+
if (newMap === this.getMap()) {
185+
return;
186+
}
187+
this.getMap().removeEntity(this);
188+
// Clear the position
189+
this._x = 0;
190+
this._y = 0;
191+
this._z = 0;
192+
// Add to the new map
193+
newMap.addEntity(this);
194+
}
177195
}

assets/entitymixins.js

Lines changed: 107 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,19 @@ Game.EntityMixins.Destructible = {
5252
},
5353
takeDamage: function(attacker, damage) {
5454
this._hp -= damage;
55-
// If have 0 or less HP, then remove ourselves from the map
55+
// If have 0 or less HP, then remove ourseles from the map
5656
if (this._hp <= 0) {
5757
Game.sendMessage(attacker, 'You kill the %s!', [this.getName()]);
58-
// If the entity is a corpse dropper, try to add a corpse
59-
if (this.hasMixin(Game.EntityMixins.CorpseDropper)) {
60-
this.tryDropCorpse();
61-
}
58+
// Raise events
59+
this.raiseEvent('onDeath', attacker);
60+
attacker.raiseEvent('onKill', this);
6261
this.kill();
63-
// Give the attacker experience points.
64-
if (attacker.hasMixin('ExperienceGainer')) {
65-
var exp = this.getMaxHp() + this.getDefenseValue();
66-
if (this.hasMixin('Attacker')) {
67-
exp += this.getAttackValue();
68-
}
69-
// Account for level differences
70-
if (this.hasMixin('ExperienceGainer')) {
71-
exp -= (attacker.getLevel() - this.getLevel()) * 3;
72-
}
73-
// Only give experience if more than 0.
74-
if (exp > 0) {
75-
attacker.giveExperience(exp);
76-
}
77-
}
62+
}
63+
},
64+
listeners: {
65+
onGainLevel: function() {
66+
// Heal the entity.
67+
this.setHp(this.getMaxHp());
7868
}
7969
}
8070
}
@@ -379,14 +369,17 @@ Game.EntityMixins.CorpseDropper = {
379369
// Chance of dropping a cropse (out of 100).
380370
this._corpseDropRate = template['corpseDropRate'] || 100;
381371
},
382-
tryDropCorpse: function() {
383-
if (Math.round(Math.random() * 100) < this._corpseDropRate) {
384-
// Create a new corpse item and drop it.
385-
this._map.addItem(this.getX(), this.getY(), this.getZ(),
386-
Game.ItemRepository.create('corpse', {
387-
name: this._name + ' corpse',
388-
foreground: this._foreground
389-
}));
372+
listeners: {
373+
onDeath: function(attacker) {
374+
// Check if we should drop a corpse.
375+
if (Math.round(Math.random() * 100) <= this._corpseDropRate) {
376+
// Create a new corpse item and drop it.
377+
this._map.addItem(this.getX(), this.getY(), this.getZ(),
378+
Game.ItemRepository.create('corpse', {
379+
name: this._name + ' corpse',
380+
foreground: this._foreground
381+
}));
382+
}
390383
}
391384
}
392385
};
@@ -561,12 +554,22 @@ Game.EntityMixins.ExperienceGainer = {
561554
// Check if we gained at least one level.
562555
if (levelsGained > 0) {
563556
Game.sendMessage(this, "You advance to level %d.", [this._level]);
564-
// Heal the entity if possible.
565-
if (this.hasMixin('Destructible')) {
566-
this.setHp(this.getMaxHp());
557+
this.raiseEvent('onGainLevel');
558+
}
559+
},
560+
listeners: {
561+
onKill: function(victim) {
562+
var exp = victim.getMaxHp() + victim.getDefenseValue();
563+
if (victim.hasMixin('Attacker')) {
564+
exp += victim.getAttackValue();
567565
}
568-
if (this.hasMixin('StatGainer')) {
569-
this.onGainLevel();
566+
// Account for level differences
567+
if (victim.hasMixin('ExperienceGainer')) {
568+
exp -= (this.getLevel() - victim.getLevel()) * 3;
569+
}
570+
// Only give experience if more than 0.
571+
if (exp > 0) {
572+
this.giveExperience(exp);
570573
}
571574
}
572575
}
@@ -575,24 +578,83 @@ Game.EntityMixins.ExperienceGainer = {
575578
Game.EntityMixins.RandomStatGainer = {
576579
name: 'RandomStatGainer',
577580
groupName: 'StatGainer',
578-
onGainLevel: function() {
579-
var statOptions = this.getStatOptions();
580-
// Randomly select a stat option and execute the callback for each
581-
// stat point.
582-
while (this.getStatPoints() > 0) {
583-
// Call the stat increasing function with this as the context.
584-
statOptions.random()[1].call(this);
585-
this.setStatPoints(this.getStatPoints() - 1);
581+
listeners: {
582+
onGainLevel: function() {
583+
var statOptions = this.getStatOptions();
584+
// Randomly select a stat option and execute the callback for each
585+
// stat point.
586+
while (this.getStatPoints() > 0) {
587+
// Call the stat increasing function with this as the context.
588+
statOptions.random()[1].call(this);
589+
this.setStatPoints(this.getStatPoints() - 1);
590+
}
586591
}
587592
}
588593
};
589594

590595
Game.EntityMixins.PlayerStatGainer = {
591596
name: 'PlayerStatGainer',
592597
groupName: 'StatGainer',
593-
onGainLevel: function() {
594-
// Setup the gain stat screen and show it.
595-
Game.Screen.gainStatScreen.setup(this);
596-
Game.Screen.playScreen.setSubScreen(Game.Screen.gainStatScreen);
598+
listeners: {
599+
onGainLevel: function() {
600+
// Setup the gain stat screen and show it.
601+
Game.Screen.gainStatScreen.setup(this);
602+
Game.Screen.playScreen.setSubScreen(Game.Screen.gainStatScreen);
603+
}
597604
}
598605
};
606+
607+
Game.EntityMixins.GiantZombieActor = Game.extend(Game.EntityMixins.TaskActor, {
608+
init: function(template) {
609+
// Call the task actor init with the right tasks.
610+
Game.EntityMixins.TaskActor.init.call(this, Game.extend(template, {
611+
'tasks' : ['growArm', 'spawnSlime', 'hunt', 'wander']
612+
}));
613+
// We only want to grow the arm once.
614+
this._hasGrownArm = false;
615+
},
616+
canDoTask: function(task) {
617+
// If we haven't already grown arm and HP <= 20, then we can grow.
618+
if (task === 'growArm') {
619+
return this.getHp() <= 20 && !this._hasGrownArm;
620+
// Spawn a slime only a 10% of turns.
621+
} else if (task === 'spawnSlime') {
622+
return Math.round(Math.random() * 100) <= 10;
623+
// Call parent canDoTask
624+
} else {
625+
return Game.EntityMixins.TaskActor.canDoTask.call(this, task);
626+
}
627+
},
628+
growArm: function() {
629+
this._hasGrownArm = true;
630+
this.increaseAttackValue(5);
631+
// Send a message saying the zombie grew an arm.
632+
Game.sendMessageNearby(this.getMap(),
633+
this.getX(), this.getY(), this.getZ(),
634+
'An extra arm appears on the giant zombie!');
635+
},
636+
spawnSlime: function() {
637+
// Generate a random position nearby.
638+
var xOffset = Math.floor(Math.random() * 3) - 1;
639+
var yOffset = Math.floor(Math.random() * 3) - 1;
640+
641+
// Check if we can spawn an entity at that position.
642+
if (!this.getMap().isEmptyFloor(this.getX() + xOffset, this.getY() + yOffset,
643+
this.getZ())) {
644+
// If we cant, do nothing
645+
return;
646+
}
647+
// Create the entity
648+
var slime = Game.EntityRepository.create('slime');
649+
slime.setX(this.getX() + xOffset);
650+
slime.setY(this.getY() + yOffset)
651+
slime.setZ(this.getZ());
652+
this.getMap().addEntity(slime);
653+
},
654+
listeners: {
655+
onDeath: function(attacker) {
656+
// Switch to win screen when killed!
657+
Game.switchScreen(Game.Screen.winScreen);
658+
}
659+
}
660+
});

0 commit comments

Comments
 (0)