1 /*
2  * Copyright (c) 2017-2019 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: Copyright (c) 2017-2019 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/event/world/player.d, selery/event/world/player.d)
28  */
29 module selery.event.world.player;
30 
31 import sel.format : Format;
32 
33 import selery.block.block : Block;
34 import selery.entity.entity : Entity;
35 import selery.event.event : Cancellable;
36 import selery.event.world.damage : EntityDamageEvent;
37 import selery.event.world.entity : EntityDeathEvent;
38 import selery.event.world.world;
39 import selery.item : Slot, Item;
40 import selery.lang : Translation;
41 import selery.log : Message;
42 import selery.math.vector : BlockPosition, EntityPosition, ChunkPosition;
43 import selery.player.player : Player;
44 import selery.world.world : World;
45 
46 /**
47  * General player event
48  * It can be listened by world and players
49  */
50 interface PlayerEvent : WorldEvent {
51 
52 	public pure nothrow @property @safe @nogc Player player();
53 
54 	public static mixin template Implementation() {
55 
56 		//mixin WorldEvent.Implementation;
57 
58 		private Player n_player;
59 
60 		public final override pure nothrow @property @safe @nogc Player player() {
61 			return this.n_player;
62 		}
63 
64 		public final override pure nothrow @property @safe @nogc World world() {
65 			// override WorldEvent.Implementation to return World from Player.world
66 			return this.player.world;
67 		}
68 
69 	}
70 
71 }
72 
73 /** General announce event */
74 abstract class PlayerAnnounceEvent : PlayerEvent {
75 
76 	mixin PlayerEvent.Implementation;
77 	
78 	private Message[] def;
79 	private Message[] m_message;
80 	
81 	public @safe @nogc this(Player player, Message[] message, bool announce) {
82 		this.n_player = player;
83 		this.def = message;
84 		if(announce) this.m_message = message;
85 	}
86 	
87 	public final pure nothrow @property @safe @nogc Message[] message() {
88 		return this.m_message;
89 	}
90 	
91 	public final pure nothrow @property @safe @nogc Message[] message(Message[] message) {
92 		return this.m_message = message;
93 	}
94 
95 	public final @property Message[] message(string message) {
96 		return this.m_message = [Message(message)];
97 	}
98 	
99 	public final @property @safe Message[] message(bool display) {
100 		if(display) this.m_message = this.def;
101 		else this.m_message.length = 0;
102 		return this.m_message;
103 	}
104 	
105 	public final pure nothrow @property @safe @nogc bool announce() {
106 		return this.m_message.length > 0;
107 	}
108 
109 	public final @property @safe bool announce(bool announce) {
110 		this.message = announce;
111 		return this.announce;
112 	}
113 	
114 }
115 
116 /** Called when a player spawns, but it isn't spawned to the other entities/players yet */
117 final class PlayerSpawnEvent : PlayerAnnounceEvent {
118 	
119 	public bool spawn = true;
120 	
121 	public this(Player player, bool announce) {
122 		super(player, Message.convert(Format.yellow, Translation("multiplayer.player.joined", player.displayName)), announce);
123 	}
124 	
125 }
126 
127 /** Called when a player leaves the world, but it isn't despawned to the other entities/players yet */
128 final class PlayerDespawnEvent : PlayerAnnounceEvent {
129 
130 	public this(Player player, bool announce) {
131 		super(player, Message.convert(Format.yellow, Translation("multiplayer.player.left", player.displayName)), announce);
132 	}
133 	
134 }
135 
136 /** Called when the respawn button is clicked */
137 final class PlayerRespawnEvent : PlayerEvent, Cancellable {
138 
139 	mixin Cancellable.Implementation;
140 
141 	mixin PlayerEvent.Implementation;
142 
143 	public @safe @nogc this(Player player) {
144 		this.n_player = player;
145 	}
146 
147 }
148 
149 /**
150  * Called when a player sends a chat message
151  * Example:
152  * ---
153  * public @event void chat(PlayerChatEvent event) {
154  * 
155  *    // <player> message
156  *    event.format = (string name, string message){ return "<" ~ name ~ "> " ~ message; };
157  * 
158  *    // player: message
159  *    event.format = (string name, string message){ return name ~ ": " ~ message; };
160  * 
161  *    // replace bad words
162  *    event.message = event.message.replaceAll(ctRegex!("fuck", "i"), "f**k");
163  *    
164  * }
165  * ---
166  */
167 final class PlayerChatEvent : PlayerEvent, Cancellable {
168 
169 	mixin Cancellable.Implementation;
170 
171 	mixin PlayerEvent.Implementation;
172 	
173 	public string message;
174 	public string delegate(string, string) format;
175 	
176 	public pure nothrow @safe @nogc this(Player player, string message) {
177 		this.n_player = player;
178 		this.message = message;
179 	}
180 	
181 }
182 
183 /**
184  * Player movement, called when a player sends a movement packet
185  */
186 final class PlayerMoveEvent : PlayerEvent, Cancellable {
187 
188 	mixin Cancellable.Implementation;
189 
190 	mixin PlayerEvent.Implementation;
191 	
192 	private EntityPosition n_position;
193 	private float n_yaw, n_body_yaw, n_pitch;
194 	private bool mov_in_space = true;
195 	
196 	public @safe this(Player player, EntityPosition position, float yaw, float bodyYaw, float pitch) {
197 		this.n_player = player;
198 		this.n_position = position;
199 		this.n_yaw = yaw % 360;
200 		this.n_body_yaw = bodyYaw % 360;
201 		this.n_pitch = pitch < -90 ? -90 : (pitch > 90 ? 90 : pitch);
202 		if(player.position == position) {
203 			this.mov_in_space = false;
204 		}
205 	}
206 	
207 	public @property @safe @nogc EntityPosition position() {
208 		return this.n_position;
209 	}
210 	
211 	public @property @safe @nogc float yaw() {
212 		return this.n_yaw;
213 	}
214 	
215 	public @property @safe @nogc float bodyYaw() {
216 		return this.n_body_yaw;
217 	}
218 	
219 	public @property @safe @nogc float pitch() {
220 		return this.n_pitch;
221 	}
222 	
223 	public @property @safe @nogc bool rotation() {
224 		return !this.mov_in_space;
225 	}
226 	
227 	public @property @safe @nogc bool space() {
228 		return this.mov_in_space;
229 	}
230 	
231 }
232 
233 /** Called when a player hits the jump button */
234 final class PlayerJumpEvent : PlayerEvent {
235 
236 	mixin PlayerEvent.Implementation;
237 	
238 	public @safe @nogc this(Player player) {
239 		this.n_player = player;
240 	}
241 	
242 }
243 
244 abstract class PlayerSprintingEvent : PlayerEvent, Cancellable {
245 
246 	mixin Cancellable.Implementation;
247 
248 	mixin PlayerEvent.Implementation;
249 
250 	public @safe @nogc this(Player player) {
251 		this.n_player = player;
252 	}
253 
254 }
255 
256 final class PlayerStartSprintingEvent : PlayerSprintingEvent {
257 	
258 	public @safe @nogc this(Player player) {
259 		super(player);
260 	}
261 	
262 }
263 
264 final class PlayerStopSprintingEvent : PlayerSprintingEvent {
265 	
266 	public @safe @nogc this(Player player) {
267 		super(player);
268 	}
269 	
270 }
271 
272 abstract class PlayerSneakingEvent : PlayerEvent, Cancellable {
273 	
274 	mixin Cancellable.Implementation;
275 	
276 	mixin PlayerEvent.Implementation;
277 	
278 	public @safe @nogc this(Player player) {
279 		this.n_player = player;
280 	}
281 	
282 }
283 
284 final class PlayerStartSneakingEvent : PlayerSneakingEvent {
285 	
286 	public @safe @nogc this(Player player) {
287 		super(player);
288 	}
289 	
290 }
291 
292 final class PlayerStopSneakingEvent : PlayerSneakingEvent {
293 	
294 	public @safe @nogc this(Player player) {
295 		super(player);
296 	}
297 	
298 }
299 
300 final class PlayerDeathEvent : EntityDeathEvent, PlayerEvent {
301 
302 	private Player n_player;
303 
304 	public @safe @nogc this(Player player, EntityDamageEvent cause) {
305 		super(player, cause);
306 		this.message = true;
307 		this.n_player = player;
308 	}
309 
310 	public override pure nothrow @property @safe @nogc Player player() {
311 		return this.n_player;
312 	}
313 
314 }
315 
316 final class PlayerAnimationEvent : PlayerEvent, Cancellable {
317 
318 	mixin Cancellable.Implementation;
319 
320 	mixin PlayerEvent.Implementation;
321 	
322 	public @safe @nogc this(Player player) {
323 		this.n_player = player;
324 	}
325 	
326 }
327 
328 final class PlayerUpdateViewDistanceEvent : PlayerEvent {
329 
330 	mixin PlayerEvent.Implementation;
331 	
332 	private uint n_from;
333 	public uint to;
334 	
335 	public @safe @nogc this(Player player, uint from, uint to) {
336 		this.n_player = player;
337 		this.n_from = from;
338 		this.to = to;
339 	}
340 	
341 	public @property @safe @nogc uint from() {
342 		return this.n_from;
343 	}
344 	
345 }
346 
347 final class PlayerNeedChunkEvent : PlayerEvent, Cancellable {
348 
349 	mixin Cancellable.Implementation;
350 
351 	mixin PlayerEvent.Implementation;
352 
353 	private ChunkPosition n_chunk;
354 	
355 	public @safe @nogc this(Player player, ChunkPosition chunk) {
356 		this.n_player = player;
357 		this.n_chunk = chunk;
358 	}
359 	
360 	public @property @safe @nogc ChunkPosition chunk() {
361 		return this.n_chunk;
362 	}
363 	
364 }
365 
366 final class PlayerRequestMapEvent : PlayerEvent, Cancellable {
367 
368 	mixin Cancellable.Implementation;
369 
370 	mixin PlayerEvent.Implementation;
371 
372 	public immutable ushort mapId;
373 
374 	public @safe @nogc this(Player player, ushort mapId) {
375 		this.n_player = player;
376 		this.mapId = mapId;
377 	}
378 
379 }
380 
381 final class PlayerAfterSpawnEvent : PlayerEvent {
382 
383 	mixin PlayerEvent.Implementation;
384 
385 	public @safe @nogc this(Player player) {
386 		this.n_player = player;
387 	}
388 
389 }
390 
391 final class PlayerAfterDespawnEvent : PlayerEvent {
392 
393 	mixin PlayerEvent.Implementation;
394 
395 	public @safe @nogc this(Player player) {
396 		this.n_player = player;
397 	}
398 
399 }
400 
401 final class PlayerBreakBlockEvent : PlayerEvent, Cancellable {
402 
403 	mixin Cancellable.Implementation;
404 
405 	mixin PlayerEvent.Implementation;
406 
407 	private Block n_block;
408 	private BlockPosition n_position;
409 	public bool drop = true;
410 	public bool consumeItem = true;
411 	public bool removeBlock = true;
412 	public bool particles = true;
413 
414 	public @safe @nogc this(Player player, Block block, BlockPosition position) {
415 		this.n_player = player;
416 		this.n_block = block;
417 		this.n_position = position;
418 	}
419 
420 	public @property @safe @nogc Block block() {
421 		return this.n_block;
422 	}
423 
424 	public @property @safe @nogc BlockPosition position() {
425 		return this.n_position;
426 	}
427 
428 }
429 
430 final class PlayerPlaceBlockEvent : PlayerEvent, Cancellable {
431 
432 	mixin Cancellable.Implementation;
433 
434 	mixin PlayerEvent.Implementation;
435 
436 	private BlockPosition n_position;
437 	private Slot n_slot;
438 	public immutable uint face;
439 
440 	public @safe @nogc this(Player player, Slot slot, BlockPosition position, uint face) {
441 		this.n_player = player;
442 		this.n_slot = slot;
443 		this.n_position = position;
444 		this.face = face;
445 	}
446 
447 	public @property @safe @nogc Slot slot() {
448 		return this.n_slot;
449 	}
450 
451 	public @property @safe @nogc Item item() {
452 		return this.n_slot.item;
453 	}
454 
455 	public @property @safe @nogc BlockPosition position() {
456 		return this.n_position;
457 	}
458 
459 }
460 
461 /*class PlayerPickupEntityEvent : PlayerCancellableEvent {
462 
463 	private Entity n_entity;
464 
465 	public @safe @nogc this(Player player, Entity entity) {
466 		super(player);
467 		this.n_entity = entity;
468 	}
469 
470 	public final @property @safe @nogc Entity entity() {
471 		return this.n_entity;
472 	}
473 
474 }
475 
476 final class PlayerPickupItemEvent : PlayerPickupEntityEvent {
477 
478 	private Slot n_slot;
479 
480 	public @safe @nogc this(Player player, ItemEntity item) {
481 		super(player, item);
482 		this.n_slot = item.slot;
483 	}
484 
485 	public @property @safe @nogc Slot slot() {
486 		return this.n_slot;
487 	}
488 
489 	public @property @safe @nogc Item item() {
490 		return this.n_slot.item;
491 	}
492 
493 }*/
494 
495 final class PlayerDropItemEvent : PlayerEvent, Cancellable {
496 
497 	mixin Cancellable.Implementation;
498 
499 	mixin PlayerEvent.Implementation;
500 
501 	private Slot n_slot;
502 
503 	public @safe @nogc this(Player player, Slot slot) {
504 		this.n_player = player;
505 		this.n_slot = slot;
506 	}
507 
508 	public @property @safe @nogc Slot slot() {
509 		return this.n_slot;
510 	}
511 
512 }