1 /*
2  * Copyright (c) 2017-2018 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: 2017-2018 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/event/world/damage.d, selery/event/world/damage.d)
28  */
29 module selery.event.world.damage;
30 
31 import std.algorithm : max;
32 
33 import selery.block.block : Block;
34 import selery.effect;
35 import selery.enchantment;
36 import selery.entity.entity : Entity;
37 import selery.entity.human : Human;
38 import selery.entity.interfaces;
39 import selery.entity.living : Living;
40 import selery.entity.noai : Lightning;
41 import selery.event.event : Cancellable;
42 import selery.event.world.entity : EntityEvent;
43 import selery.event.world.player : PlayerEvent;
44 import selery.item.item : Item;
45 import selery.item.slot : Slot;
46 import selery.item.tool : Tools;
47 import selery.lang : Translation, Translatable;
48 import selery.math.vector;
49 import selery.player.player : Player;
50 import selery.world.world : World;
51 
52 private enum Modifiers : size_t {
53 
54 	none = 0,
55 
56 	resistance = 1 << 0,
57 	falling = 1 << 1,
58 	armor = 1 << 2,
59 	fire = 1 << 3,
60 	blast = 1 << 4,
61 	projectile = 1 << 5,
62 	
63 	all = size_t.max
64 
65 }
66 
67 // damage caused by itself
68 interface EntityDamageEvent : EntityEvent, Cancellable {
69 	
70 	public pure nothrow @property @safe @nogc Entity victim();
71 	
72 	public pure nothrow @property @safe @nogc float originalDamage();
73 	
74 	public pure nothrow @property @safe @nogc float damage();
75 	
76 	public pure nothrow @property @safe @nogc float damage(float damage);
77 
78 	public pure nothrow @property @safe @nogc bool imminent();
79 	
80 	public pure nothrow @property @safe @nogc Translation message();
81 	
82 	public static mixin template Implementation(size_t modifiers) {
83 
84 		mixin Cancellable.Implementation;
85 
86 		mixin EntityEvent.Implementation;
87 		
88 		private float n_original_damage;
89 		private float m_damage;
90 		
91 		protected Translation n_message;
92 		
93 		protected @safe entityDamage(Entity entity, float damage, Translatable translatable) {
94 			this.n_entity = entity;
95 			this.n_original_damage = damage;
96 			this.calculateDamage();
97 			this.n_message = Translation(translatable, entity.displayName);
98 		}
99 		
100 		protected @safe void calculateDamage() {
101 			
102 			static if(modifiers != Modifiers.none) {
103 			
104 				float damage = this.originalDamage;
105 				
106 				if(cast(Living)this.entity) {
107 				
108 					Living victim = cast(Living)this.entity;
109 					
110 					static if(modifiers & Modifiers.resistance) {
111 						if(Effect* resistance = (Effects.resistance in victim)) {
112 							damage /= 1.2 * (*resistance).levelFromOne;
113 						}
114 					}
115 					
116 					static if(modifiers & Modifiers.falling) {
117 						if(Effect* jumpBoost = (Effects.jumpBoost in victim)) {
118 							damage -= (*jumpBoost).levelFromOne;
119 						}
120 					}
121 					
122 					static if(modifiers & Modifiers.armor) {
123 						if(cast(Human)victim) {
124 							Human human = cast(Human)victim;
125 							
126 							float protection = human.inventory.protection;
127 							damage *= 1f - max(protection / 5f, protection - damage / 2f) / 25f;
128 							
129 							float epf = 0f;
130 							foreach(size_t i, Slot slot; human.inventory.armor) {
131 								if(!slot.empty) {
132 									if(Enchantment* p = (Enchantments.protection in slot.item)) {
133 										epf += (*p).level;
134 									}
135 									static if(modifiers & Modifiers.fire) {
136 										if(Enchantment* fireProtection = (Enchantments.fireProtection in slot.item)) {
137 											epf += (*fireProtection).level * 2;
138 										}
139 									}
140 									static if(modifiers & Modifiers.blast) {
141 										if(Enchantment* blastProtection = (Enchantments.blastProtection in slot.item)) {
142 											epf += (*blastProtection).level * 2;
143 										}
144 									}
145 									static if(modifiers & Modifiers.projectile) {
146 										if(Enchantment* projectileProtection = (Enchantments.projectileProtection in slot.item)) {
147 											epf += (*projectileProtection).level * 2;
148 										}
149 									}
150 									static if(modifiers & Modifiers.falling) {
151 										// boots only
152 										if(i == 3) {
153 											if(Enchantment* featherFalling = (Enchantments.featherFalling in slot.item)) {
154 												epf += (*featherFalling).level * 3;
155 											}
156 										}
157 									}
158 									if(epf >= 20) {
159 										epf = 20f;
160 										break;
161 									}
162 								}
163 							}
164 							if(epf > 0) {
165 								damage *= 1f - epf / 25f;
166 							}
167 
168 						}
169 					}
170 				
171 				}
172 				
173 				this.m_damage = damage < 0 ? 0 : damage;
174 				
175 			} else {
176 				
177 				this.m_damage = this.originalDamage;
178 				
179 			}
180 			
181 		}
182 		
183 		public final override pure nothrow @property @safe @nogc Entity victim() {
184 			return this.entity;
185 		}
186 		
187 		public final override pure nothrow @property @safe @nogc float originalDamage() {
188 			return this.n_original_damage;
189 		}
190 		
191 		public final override pure nothrow @property @safe @nogc float damage() {
192 			return this.m_damage;
193 		}
194 		
195 		public final override pure nothrow @property @safe @nogc float damage(float damage) {
196 			return this.m_damage = damage;
197 		}
198 
199 		public override pure nothrow @property @safe @nogc bool imminent() {
200 			return false;
201 		}
202 		
203 		public final override pure nothrow @property @safe @nogc Translation message() {
204 			return this.n_message;
205 		}
206 		
207 	}
208 
209 }
210 
211 // damage caused by another entity
212 interface EntityDamageByEntityEvent : EntityDamageEvent {
213 	
214 	public pure nothrow @property @safe @nogc Entity damager();
215 	
216 	public pure nothrow @property @safe @nogc bool doKnockback();
217 	
218 	public pure nothrow @property @safe @nogc EntityPosition knockback();
219 	
220 	public pure nothrow @property @safe @nogc bool isCritical();
221 
222 	public static mixin template Implementation(bool impl_entity_damage_event, size_t modifiers) {
223 	
224 		static if(impl_entity_damage_event) {
225 			mixin EntityDamageEvent.Implementation!modifiers;
226 		}
227 		
228 		private Entity n_damager;
229 		protected EntityPosition n_knockback;
230 		protected float knockback_modifier = .32;
231 		protected bool n_critical;
232 		
233 		protected @safe entityDamageByEntity(Entity victim, Entity damager, float damage, Translatable translatable) {
234 			this.entityDamage(victim, this.isCritical ? damage * 1.5 : damage, translatable);
235 			this.n_damager = damager;
236 			this.n_message.parameters ~= damager.displayName;
237 		}
238 		
239 		public pure nothrow @property @safe @nogc Entity damager() {
240 			return this.n_damager;
241 		}
242 		
243 		public pure nothrow @property @safe @nogc bool doKnockback() {
244 			return this.knockback_modifier != 0;
245 		}
246 		
247 		public pure nothrow @property @safe @nogc EntityPosition knockback() {
248 			return EntityPosition(this.n_knockback.x * this.knockback_modifier, .4, this.n_knockback.z * this.knockback_modifier);
249 		}
250 		
251 		public pure nothrow @property @safe @nogc bool isCritical() {
252 			return this.n_critical;
253 		}
254 	
255 	}
256 
257 }
258 
259 // void
260 
261 class EntityDamageByVoidEvent : EntityDamageEvent {
262 
263 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
264 
265 	protected @safe this() {}
266 
267 	public @safe this(Entity entity) {
268 		this.entityDamage(entity, 4, Translatable.all("death.attack.outOfWorld"));
269 	}
270 
271 	public final override pure nothrow @property @safe @nogc bool imminent() {
272 		return true;
273 	}
274 
275 }
276 
277 final class EntityPushedIntoVoidEvent : EntityDamageByVoidEvent, EntityDamageByEntityEvent {
278 
279 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.none);
280 	
281 	public @safe this(Entity victim, Entity damager) {
282 		super();
283 		this.entityDamageByEntity(victim, damager, 4, Translatable.all("death.attack.outOfWorld")); // no message for "{0} was pushed out of the world"
284 	}
285 
286 	mixin Cancellable.FinalImplementation;
287 
288 }
289 
290 // command
291 
292 class EntityDamageByCommandEvent : EntityDamageEvent {
293 
294 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
295 	
296 	public @safe this(Entity entity) {
297 		this.entityDamage(entity, 0xDEAD, Translatable.all("death.attack.generic"));
298 	}
299 
300 	public final override pure nothrow @property @safe @nogc bool imminent() {
301 		return true;
302 	}
303 
304 }
305 
306 // attack (contact)
307 
308 class EntityDamageByEntityAttackEvent : EntityDamageByEntityEvent {
309 
310 	mixin EntityDamageByEntityEvent.Implementation!(true, Modifiers.resistance | Modifiers.armor);
311 	
312 	public @safe this(Entity victim, Entity damager, float damage) {
313 		this.entityDamageByEntity(victim, damager, damage, Translatable.all("death.attack.player"));
314 		this.n_knockback = damager.direction;
315 	}
316 	
317 	public pure nothrow @property @safe @nogc Item item() {
318 		return null;
319 	}
320 
321 }
322 
323 alias EntityAttackedByEntityEvent = EntityDamageByEntityAttackEvent;
324 
325 class EntityDamageByPlayerAttackEvent : EntityDamageByEntityAttackEvent, PlayerEvent {
326 	
327 	private Item n_item;
328 	
329 	public @trusted this(Entity victim, Player damager) {
330 		float damage = 1;
331 		if(!damager.inventory.held.empty) {
332 			this.n_item = damager.inventory.held.item;
333 			// damage from weapon and weapon's enchantments
334 			damage = this.item.attack;
335 			if(Enchantment* sharpness = (Enchantments.sharpness in this.item)) damage *= 1.2f * (*sharpness).level;
336 			if(cast(Arthropods)this.victim){ if(Enchantment* baneOfArthropods = (Enchantments.baneOfArthropods in this.item)) damage *= 2.5f * (*baneOfArthropods).level; }
337 			if(cast(Undead)this.victim){ if(Enchantment* smite = (Enchantments.smite in this.item)) damage *= 2.5f * (*smite).level; }
338 		}
339 		// effects
340 		if(Effect* strength = (Effects.strength in damager)) damage *= 1.3f * (*strength).levelFromOne;
341 		if(Effect* weakness = (Effects.weakness in damager)) damage *= .5f * (*weakness).levelFromOne;
342 		// critical
343 		this.n_critical = damager.falling && !damager.sprinting && damager.vehicle is null && Effects.blindness !in damager;
344 		// calculate damage
345 		super(victim, damager, damage);
346 		// more enchantments
347 		if(this.item !is null) {
348 			//TODO fire ench
349 			// more knockback!
350 			if(this.item.toolType == Tools.sword || this.item.toolType == Tools.axe) {
351 				this.knockback_modifier = .52;
352 			}
353 			if(Enchantment* knockback = (Enchantments.knockback in this.item)) {
354 				this.knockback_modifier += .6 * (*knockback).level;
355 			}
356 		}
357 		// add weapon's name to args
358 		if(this.item !is null && this.item.customName != "") {
359 			this.n_message.translatable = Translatable.all("death.attack.player.item");
360 			this.n_message.parameters ~= this.item.customName;
361 		}
362 	}
363 	
364 	public final override pure nothrow @property @safe @nogc Player player() {
365 		return this.playerDamager;
366 	}
367 	
368 	public final pure nothrow @property @safe @nogc Player playerDamager() {
369 		return cast(Player)this.damager;
370 	}
371 	
372 	public final override pure nothrow @property @safe @nogc Item item() {
373 		return this.n_item;
374 	}
375 
376 }
377 
378 alias EntityAttackedByPlayerEvent = EntityDamageByPlayerAttackEvent;
379 
380 final class PlayerDamageByPlayerAttackEvent : EntityDamageByPlayerAttackEvent {
381 
382 	public @safe this(Player victim, Player damager) {
383 		super(victim, damager);
384 	}
385 	
386 	public pure nothrow @property @safe @nogc Player victimPlayer() {
387 		return cast(Player)this.victim;
388 	}
389 
390 }
391 
392 alias PlayerAttackedByPlayerEvent = PlayerDamageByPlayerAttackEvent;
393 
394 // projectile
395 /*
396 // thrower (damager) can be null if the projectile has been thrown by a plugin or a dispencer
397 abstract class EntityDamageWithProjectileEvent : EntityDamageByEntityEvent {}
398 
399 final class EntityDamageWithArrowEvent : EntityDamageWithProjectileEvent {}
400 
401 final class EntityPummeledEvent : EntityDamageWithProjectileEvent {}
402 
403 class EntityDamageWithFireballEvent : EntityDamageWithProjectileEvent {}*/
404 
405 // suffocation
406 
407 final class EntitySuffocationEvent : EntityDamageEvent {
408 
409 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
410 	
411 	public @safe this(Entity entity) {
412 		this.entityDamage(entity, 1, Translatable.all("death.attack.inWall"));
413 	}
414 
415 }
416 
417 // drowning
418 
419 class EntityDrowningEvent : EntityDamageEvent {
420 
421 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
422 
423 	protected @safe @nogc this() {}
424 	
425 	public @safe this(Entity entity) {
426 		this.entityDamage(entity, 1, Translatable.all("death.attack.drown"));
427 	}
428 
429 }
430 
431 final class EntityDrowningEscapingEntityEvent : EntityDrowningEvent {
432 	
433 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.none);
434 	
435 	public @safe this(Entity victim, Entity damager) {
436 		this.entityDamageByEntity(victim, damager, 1, Translatable.all("death.attack.drown.player"));
437 	}
438 
439 }
440 
441 // explosion
442 
443 class EntityDamageByExplosionEvent : EntityDamageEvent {
444 
445 	mixin EntityDamageEvent.Implementation!(Modifiers.resistance | Modifiers.armor | Modifiers.blast);
446 
447 	protected @safe @nogc this() {}
448 	
449 	public @safe this(Entity entity, float damage) {
450 		this.entityDamage(entity, damage, Translatable.all("death.attack.explosion"));
451 	}
452 
453 }
454 
455 class EntityDamageByEntityExplosionEvent : EntityDamageByExplosionEvent, EntityDamageByEntityEvent {
456 
457 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.resistance | Modifiers.armor | Modifiers.blast);
458 	
459 	public @safe this(Entity victim, Entity damager, float damage) {
460 		this.entityDamageByEntity(victim, damager, damage, Translatable.all("death.attack.explosion.player"));
461 	}
462 
463 	mixin Cancellable.FinalImplementation;
464 
465 }
466 
467 //TODO final class EntityDamageByTntExplosionEvent : EntityDamageByEntityExplosionEvent {}
468 
469 //TODO final class EntityDamageByCreeperExplosionEvent : EntityDamageByEntityExplosionEvent {}
470 
471 // hot stuff (fire and lava)
472 
473 interface EntityDamageByHeatEvent : EntityDamageEvent {
474 
475 	public static mixin template Implementation() {
476 	
477 		mixin EntityDamageEvent.Implementation!(Modifiers.resistance | Modifiers.armor | Modifiers.fire);
478 	
479 	}
480 
481 }
482 
483 interface EntityDamageByHeatEscapingEntityEvent : EntityDamageByHeatEvent, EntityDamageByEntityEvent {
484 
485 	public static mixin template Implementation() {
486 	
487 		mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.resistance | Modifiers.armor | Modifiers.fire);
488 
489 		mixin Cancellable.FinalImplementation;
490 	
491 	}
492 
493 }
494 
495 class EntityBurningEvent : EntityDamageByHeatEvent {
496 
497 	mixin EntityDamageByHeatEvent.Implementation;
498 
499 	protected @safe @nogc this() {}
500 	
501 	public @safe this(Entity entity) {
502 		this.entityDamage(entity, 1, Translatable.all("death.attack.onFire"));
503 	}
504 
505 }
506 
507 final class EntityBurningEscapingEntityEvent : EntityBurningEvent, EntityDamageByHeatEscapingEntityEvent {
508 
509 	mixin EntityDamageByHeatEscapingEntityEvent.Implementation;
510 	
511 	public @safe this(Entity victim, Entity damager) {
512 		this.entityDamageByEntity(victim, damager, 1, Translatable.all("death.attack.onFire.player"));
513 	}
514 
515 }
516 
517 class EntityDamageByFireEvent : EntityDamageByHeatEvent {
518 
519 	mixin EntityDamageByHeatEvent.Implementation;
520 
521 	protected @safe @nogc this() {}
522 	
523 	public @safe this(Entity entity) {
524 		this.entityDamage(entity, 1, Translatable.all("death.attack.inFire"));
525 	}
526 
527 }
528 
529 final class EntityDamageByFireEscapingEntityEvent : EntityDamageByFireEvent, EntityDamageByHeatEscapingEntityEvent {
530 
531 	mixin EntityDamageByHeatEscapingEntityEvent.Implementation;
532 	
533 	public @safe this(Entity victim, Entity damager) {
534 		this.entityDamageByEntity(victim, damager, 1, Translatable.all("death.attack.inFire.player"));
535 	}
536 
537 }
538 
539 class EntityDamageByLavaEvent : EntityDamageByHeatEvent {
540 	
541 	mixin EntityDamageByHeatEvent.Implementation;
542 	
543 	protected @safe @nogc this() {}
544 	
545 	public @safe this(Entity entity) {
546 		this.entityDamage(entity, 4, Translatable.all("death.attack.lava"));
547 	}
548 	
549 }
550 
551 final class EntityDamageByLavaEscapingEntityEvent : EntityDamageByLavaEvent, EntityDamageByHeatEscapingEntityEvent {
552 	
553 	mixin EntityDamageByHeatEscapingEntityEvent.Implementation;
554 	
555 	public @safe this(Entity victim, Entity damager) {
556 		this.entityDamageByEntity(victim, damager, 4, Translatable.all("death.attack.lava.player"));
557 	}
558 	
559 }
560 
561 class EntityDamageByMagmaBlockEvent : EntityDamageByHeatEvent {
562 	
563 	mixin EntityDamageByHeatEvent.Implementation;
564 	
565 	protected @safe @nogc this() {}
566 	
567 	public @safe this(Entity entity) {
568 		this.entityDamage(entity, 4, Translatable.all("death.attack.magmaBlock"));
569 	}
570 	
571 }
572 
573 final class EntityDamageByMagmaBlockEscapingEntityEvent : EntityDamageByMagmaBlockEvent, EntityDamageByHeatEscapingEntityEvent {
574 	
575 	mixin EntityDamageByHeatEscapingEntityEvent.Implementation;
576 	
577 	public @safe this(Entity victim, Entity damager) {
578 		this.entityDamageByEntity(victim, damager, 4, Translatable.all("death.attack.magmaBlock.player"));
579 	}
580 	
581 }
582 
583 // magic
584 
585 class EntityDamageByMagicEvent : EntityDamageEvent {
586 
587 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
588 
589 	protected @safe @nogc this() {}
590 	
591 	public @safe this(Entity entity, float damage) {
592 		this.entityDamage(entity, damage, Translatable.all("death.attack.magic"));
593 	}
594 
595 }
596 
597 final class EntityDamageWithMagicEvent : EntityDamageByMagicEvent, EntityDamageByEntityEvent {
598 
599 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.none);
600 	
601 	public @safe this(Entity victim, Entity damager, float damage) {
602 		this.entityDamageByEntity(victim, damager, damage, Translatable.all("death.attack.indirectMagic"));
603 	}
604 
605 	mixin Cancellable.FinalImplementation;
606 
607 }
608 
609 // poison
610 
611 final class EntityDamageByPoisonEvent : EntityDamageEvent {
612 	
613 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
614 	
615 	public @safe this(Entity entity) {
616 		this.entityDamage(entity, 1, Translatable.init); // no message (can't die poisoned)
617 	}
618 
619 }
620 
621 // wither
622 
623 final class EntityDamageByWitherEffectEvent : EntityDamageEvent {
624 
625 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
626 	
627 	public @safe this(Entity entity) {
628 		this.entityDamage(entity, 1, Translatable.all("death.attack.wither"));
629 	}
630 
631 }
632 
633 // lightning
634 
635 final class EntityStruckByLightningEvent : EntityDamageEvent {
636 
637 	mixin EntityDamageEvent.Implementation!(Modifiers.resistance | Modifiers.armor);
638 	
639 	private Lightning n_lightning;
640 	
641 	public @safe this(Entity entity, Lightning lightning) {
642 		this.entityDamage(entity, 5, Translatable.all("death.attack.lightning"));
643 		this.n_lightning = lightning;
644 	}
645 	
646 	public pure nothrow @property @safe @nogc Lightning lightning() {
647 		return this.n_lightning;
648 	}
649 	
650 	public pure nothrow @property @safe @nogc EntityPosition position() {
651 		return this.lightning.position;
652 	}
653 
654 }
655 
656 // thorns
657 
658 final class EntityDamageByThornsEvent : EntityDamageByEntityEvent {
659 
660 	mixin EntityDamageByEntityEvent.Implementation!(true, Modifiers.resistance | Modifiers.armor);
661 	
662 	public @safe this(Entity victim, Entity damager, float damage) {
663 		this.entityDamageByEntity(victim, damager, damage, Translatable.all("death.attack.thorns"));
664 	}
665 
666 }
667 
668 // starvation
669 
670 final class EntityStarveEvent : EntityDamageEvent {
671 
672 	mixin EntityDamageEvent.Implementation!(Modifiers.none);
673 	
674 	public @safe this(Entity entity) {
675 		this.entityDamage(entity, 1, Translatable.all("death.attack.starve"));
676 	}
677 
678 }
679 
680 // falling block (is the block already placed?)
681 
682 class EntitySquashedByFallingBlockEvent : EntityDamageEvent {
683 	
684 	mixin EntityDamageEvent.Implementation!(Modifiers.armor);
685 	
686 	private Block n_block;
687 	
688 	public @safe this(Entity entity, Block block, float damage, const Translatable message=Translatable.all("death.attack.fallingBlock")) {
689 		this.entityDamage(entity, damage, message);
690 	}
691 
692 }
693 
694 /*final class EntitySquashedByAnvilEvent : EntitySquashedByFallingBlockEvent {
695 
696 	public @safe this(Entity entity, Blocks.Anvil anvil, float damage) {
697 		super(entity, anvil, damage, Translatable.all("death.attack.anvil"));
698 	}
699 
700 }*/
701 
702 // cactus
703 
704 class EntityDamageByCactusEvent : EntityDamageEvent {
705 
706 	mixin EntityDamageEvent.Implementation!(Modifiers.resistance | Modifiers.armor);
707 
708 	protected @safe @nogc this() {}
709 	
710 	public @safe this(Entity entity) {
711 		this.entityDamage(entity, 1, Translatable.all("death.attack.cactus"));
712 	}
713 	
714 }
715 
716 final class EntityDamageByCactusEscapingEntityEvent : EntityDamageByCactusEvent, EntityDamageByEntityEvent {
717 
718 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.resistance | Modifiers.armor);
719 	
720 	public @safe this(Entity victim, Entity damager) {
721 		this.entityDamageByEntity(victim, damager, 1, Translatable.all("death.attack.cactus.player"));
722 	}
723 
724 	mixin Cancellable.FinalImplementation;
725 
726 }
727 
728 // falling
729 
730 class EntityFallDamageEvent : EntityDamageEvent {
731 
732 	mixin EntityDamageEvent.Implementation!(Modifiers.falling);
733 
734 	protected @safe @nogc this() {}
735 	
736 	public @safe this(Entity entity, float damage) {
737 		this.entityDamage(entity, damage, Translatable.all("death.attack.fall"));
738 	}
739 	
740 }
741 
742 final class EntityDoomedToFallEvent : EntityFallDamageEvent, EntityDamageByEntityEvent {
743 
744 	mixin EntityDamageByEntityEvent.Implementation!(false, Modifiers.falling);
745 	
746 	public @safe this(Entity victim, Entity damager, float damage) {
747 		this.entityDamageByEntity(victim, damager, damage, Translatable.all("death.fell.assist"));
748 	}
749 
750 	mixin Cancellable.FinalImplementation;
751 
752 }