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