-
Notifications
You must be signed in to change notification settings - Fork 23
/
act.movement.c
executable file
·4232 lines (3757 loc) · 125 KB
/
act.movement.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**************************************************************************
* File: act.movement.c Part of LuminariMUD *
* Usage: Movement commands, door handling, & sleep/rest/etc state. *
* *
* All rights reserved. See license complete information. *
* *
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
**************************************************************************/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "house.h"
#include "constants.h"
#include "dg_scripts.h"
#include "act.h"
#include "fight.h"
#include "oasis.h" /* for buildwalk */
#include "spec_procs.h"
#include "mud_event.h"
#include "hlquest.h"
#include "mudlim.h"
#include "wilderness.h" /* Wilderness! */
#include "actions.h"
#include "traps.h" /* for check_traps() */
#include "spell_prep.h"
#include "trails.h"
#include "assign_wpn_armor.h"
#include "encounters.h"
#include "hunts.h"
#include "class.h"
#include "transport.h"
/* do_gen_door utility functions */
static int find_door(struct char_data *ch, const char *type, char *dir, const char *cmdname);
static void do_doorcmd(struct char_data *ch, struct obj_data *obj, int door, int scmd);
static int ok_pick(struct char_data *ch, obj_vnum keynum, int pickproof, int scmd, int door);
#define DOOR_IS_OPENABLE(ch, obj, door) ((obj) ? (((GET_OBJ_TYPE(obj) == \
ITEM_CONTAINER) || \
GET_OBJ_TYPE(obj) == ITEM_AMMO_POUCH) && \
OBJVAL_FLAGGED(obj, CONT_CLOSEABLE)) \
: (EXIT_FLAGGED(EXIT(ch, door), EX_ISDOOR)))
#define DOOR_IS_OPEN(ch, obj, door) ((obj) ? (!OBJVAL_FLAGGED(obj, \
CONT_CLOSED)) \
: (!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED)))
#define DOOR_IS_UNLOCKED(ch, obj, door) ((obj) ? (!OBJVAL_FLAGGED(obj, \
CONT_LOCKED)) \
: (!EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED) && !EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED_EASY) && \
!EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED_MEDIUM) && !EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED_HARD)))
#define DOOR_IS_PICKPROOF(ch, obj, door) ((obj) ? (OBJVAL_FLAGGED(obj, \
CONT_PICKPROOF)) \
: (EXIT_FLAGGED(EXIT(ch, door), EX_PICKPROOF)))
#define DOOR_IS_CLOSED(ch, obj, door) (!(DOOR_IS_OPEN(ch, obj, door)))
#define DOOR_IS_LOCKED(ch, obj, door) (!(DOOR_IS_UNLOCKED(ch, obj, door)))
#define DOOR_KEY(ch, obj, door) ((obj) ? ((GET_OBJ_TYPE(obj) == ITEM_TREASURE_CHEST) ? 0 : GET_OBJ_VAL(obj, 2)) : (EXIT(ch, door)->key))
/***** start file body *****/
/* doorbash - unfinished */
/*
ACMD(do_doorbash) {
bool failure = FALSE;
int door;
struct room_direction_data *back = 0;
int other_room;
struct obj_data *obj = 0;
if (!INN_FLAGGED(ch, INNATE_DOORBASH)) {
send_to_char("But you are way too small to attempt that.\r\n", ch);
return;
}
one_argument(argument, arg);
if (!*arg) {
send_to_char("Doorbash which direction?\r\n", ch);
return;
}
door = search_block(arg, dirs, FALSE);
if (door < 0) {
send_to_char("That is not a direction!\r\n", ch);
return;
}
if (!EXIT(ch, door) || EXIT_FLAGGED(EXIT(ch, door), EX_HIDDEN)) {
send_to_char("There is nothing to doorbash in that direction.\r\n", ch);
return;
}
if (!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED)) {
send_to_char("But that direction does not need to be doorbashed.\r\n", ch);
return;
}
if ((other_room = EXIT(ch, door)->to_room) != NOWHERE) {
back = world[other_room].dir_option[rev_dir[door]];
if (back && back->to_room != ch->in_room)
back = 0;
}
if (EXIT_FLAGGED(EXIT(ch, door), EX_PICKPROOF) || dice(1, 300) > GET_R_STR(ch) + GET_LEVEL(ch)
|| EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED2) || EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED3)
)
failure = TRUE;
act("$n charges straight into the door with $s entire body.", FALSE, ch, 0, 0, TO_ROOM);
act("You throw your entire body at the door.", FALSE, ch, 0, 0, TO_CHAR);
if (failure) {
act("But it holds steady against the onslaught.", FALSE, ch, 0, 0, TO_ROOM);
act("But it holds steady against the onslaught.", FALSE, ch, 0, 0, TO_CHAR);
return;
}
act("and it shatters into a million pieces!!", FALSE, ch, 0, 0, TO_ROOM);
act("and it shatters into a million pieces!!.", FALSE, ch, 0, 0, TO_CHAR);
UNLOCK_DOOR(ch->in_room, obj, door);
OPEN_DOOR(ch->in_room, obj, door);
if (back) {
UNLOCK_DOOR(other_room, obj, rev_dir[door]);
OPEN_DOOR(other_room, obj, rev_dir[door]);
REMOVE_BIT(back->exit_info, EX_HIDDEN);
}
WAIT_STATE(ch, 1 * PULSE_VIOLENCE);
check_trap(ch, TRAP_TYPE_OPEN_DOOR, ch->in_room, 0, door);
return;
}
*/
/* checks if target ch is first-in-line in singlefile room */
bool is_top_of_room_for_singlefile(struct char_data *ch, int dir)
{
bool exit = FALSE;
int i;
for (i = 0; i < 6; i++)
{
if (EXIT(ch, i))
{
if (exit == FALSE && dir == i)
return TRUE;
exit = TRUE;
}
}
return FALSE;
}
/* function to find which char is ahead of the next
in a singlefile room */
struct char_data *get_char_ahead_of_me(struct char_data *ch, int dir)
{
struct char_data *tmp;
if (is_top_of_room_for_singlefile(ch, dir))
{
tmp = world[ch->in_room].people;
while (tmp)
{
if (tmp->next_in_room == ch)
return tmp;
tmp = tmp->next_in_room;
}
return 0;
}
return ch->next_in_room;
}
/* falling system */
/* TODO objects */
/* this function will check whether a obj should fall or not based on
circumstances and whether the obj is floating */
bool obj_should_fall(struct obj_data *obj)
{
int falling = FALSE;
if (!obj)
return FALSE;
if (ROOM_FLAGGED(obj->in_room, ROOM_FLY_NEEDED) && EXIT_OBJ(obj, DOWN))
falling = TRUE;
if (OBJ_FLAGGED(obj, ITEM_FLOAT))
{
act("You watch as $p floats gracefully in the air!",
FALSE, 0, obj, 0, TO_ROOM);
return FALSE;
}
return falling;
}
/* this function will check whether a char should fall or not based on
circumstances and whether the ch is flying / levitate */
bool char_should_fall(struct char_data *ch, bool silent)
{
int falling = FALSE;
if (!ch)
return FALSE;
if (IN_ROOM(ch) == NOWHERE)
return FALSE;
if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_FLY_NEEDED) && EXIT(ch, DOWN))
falling = TRUE;
if (RIDING(ch) && is_flying(RIDING(ch)))
{
if (!silent)
send_to_char(ch, "Your mount flies gracefully through the air...\r\n");
return FALSE;
}
if (is_flying(ch))
{
if (!silent)
send_to_char(ch, "You fly gracefully through the air...\r\n");
return FALSE;
}
if (AFF_FLAGGED(ch, AFF_LEVITATE))
{
if (!silent)
send_to_char(ch, "You levitate above the ground...\r\n");
return FALSE;
}
return falling;
}
EVENTFUNC(event_falling)
{
struct mud_event_data *pMudEvent = NULL;
struct char_data *ch = NULL;
int height_fallen = 0;
char buf[50] = {'\0'};
/* This is just a dummy check, but we'll do it anyway */
if (event_obj == NULL)
return 0;
/* For the sake of simplicity, we will place the event data in easily
* referenced pointers */
pMudEvent = (struct mud_event_data *)event_obj;
/* nab char data */
ch = (struct char_data *)pMudEvent->pStruct;
/* dummy checks */
if (!ch)
return 0;
if (!IS_NPC(ch) && !IS_PLAYING(ch->desc))
return 0;
/* retrieve svariables and convert it */
height_fallen += atoi((char *)pMudEvent->sVariables);
send_to_char(ch, "AIYEE!!! You have fallen %d feet!\r\n", height_fallen);
/* already checked if there is a down exit, lets move the char down */
do_simple_move(ch, DOWN, FALSE);
send_to_char(ch, "You fall into a new area!\r\n");
act("$n appears from above, arms flailing helplessly as $e falls...",
FALSE, ch, 0, 0, TO_ROOM);
height_fallen += 20; // 20 feet per room right now
/* can we continue this fall? */
if (!ROOM_FLAGGED(ch->in_room, ROOM_FLY_NEEDED) || !CAN_GO(ch, DOWN))
{
if (AFF_FLAGGED(ch, AFF_SAFEFALL))
{
send_to_char(ch, "Moments before slamming into the ground, a 'safefall'"
" enchantment stops you!\r\n");
act("Moments before $n slams into the ground, some sort of magical force"
" stops $s from the impact.",
FALSE, ch, 0, 0, TO_ROOM);
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_SAFEFALL);
return 0;
}
/* potential damage */
int dam = dice((height_fallen / 5), 6) + 20;
/* check for slow-fall! */
if (!IS_NPC(ch) && HAS_FEAT(ch, FEAT_SLOW_FALL))
{
dam -= 21;
dam -= dice((HAS_FEAT(ch, FEAT_SLOW_FALL) * 4), 6);
}
if (HAS_FEAT(ch, FEAT_DRACONIAN_CONTROLLED_FALL))
{
dam /= 2;
}
if (dam <= 0)
{ /* woo! avoided damage */
send_to_char(ch, "You gracefully land on your feet from your perilous fall!\r\n");
act("$n comes falling in from above, but at the last minute, pulls of an acrobatic flip and lands gracefully on $s feet!", FALSE, ch, 0, 0, TO_ROOM);
return 0; // end event
}
else
{ /* ok we know damage is going to be suffered at this stage */
send_to_char(ch, "You fall headfirst to the ground! OUCH!\r\n");
act("$n crashes into the ground headfirst, OUCH!", FALSE, ch, 0, 0, TO_ROOM);
change_position(ch, POS_RECLINING);
start_action_cooldown(ch, atSTANDARD, 12 RL_SEC);
/* we have a special situation if you die, the event will get cleared */
if (dam >= GET_HIT(ch) + 9)
{
GET_HIT(ch) = -999;
send_to_char(ch, "You attempt to scream in horror as your skull slams "
"into the ground, the very brief sensation of absolute pain "
"strikes you as all your upper-body bones shatter and your "
"head splatters all over the area!\r\n");
act("$n attempts to scream in horror as $s skull slams "
"into the ground. There is the sound like the cracking of a "
"ripe melon. You watch as all of $s upper-body bones shatter and $s "
"head splatters all over the area!\r\n",
FALSE, ch, 0, 0, TO_ROOM);
return 0;
}
else
{
damage(ch, ch, dam, TYPE_UNDEFINED, DAM_FORCE, FALSE);
return 0; // end event
}
}
}
/* hitting ground or fixing your falling situation is the only way to stop
* this event :P
* theoritically the player now could try to cast a spell, use an item, hop
* on a mount to fix his falling situation, so we gotta check if he's still
* falling every event call
* */
if (char_should_fall(ch, FALSE))
{
send_to_char(ch, "You fall tumbling down!\r\n");
act("$n drops from sight.", FALSE, ch, 0, 0, TO_ROOM);
/* are we falling more? then we gotta increase the heigh fallen */
snprintf(buf, sizeof(buf), "%d", height_fallen);
/* Need to free the memory, if we are going to change it. */
if (pMudEvent->sVariables)
free(pMudEvent->sVariables);
pMudEvent->sVariables = strdup(buf);
return (1 * PASSES_PER_SEC);
}
else
{ // stop falling!
send_to_char(ch, "You put a stop to your fall!\r\n");
act("$n turns on the air-brakes from a plummet!", FALSE, ch, 0, 0, TO_ROOM);
return 0;
}
return 0;
}
/* END falling system */
/* simple function to determine if char can walk on water (or swim through it )*/
int has_boat(struct char_data *ch, room_rnum going_to)
{
struct obj_data *obj;
int i;
if (GET_LEVEL(ch) >= LVL_IMMORT)
return (1);
if (AFF_FLAGGED(ch, AFF_WATERWALK) || is_flying(ch) ||
AFF_FLAGGED(ch, AFF_LEVITATE))
return (1);
if (RIDING(ch))
{
if (AFF_FLAGGED(RIDING(ch), AFF_WATERWALK))
return TRUE;
if (AFF_FLAGGED(RIDING(ch), AFF_LEVITATE))
return TRUE;
}
/* non-wearable boats in inventory will do it */
for (obj = ch->carrying; obj; obj = obj->next_content)
if (GET_OBJ_TYPE(obj) == ITEM_BOAT && (find_eq_pos(ch, obj, NULL) < 0))
return (1);
/* and any boat you're wearing will do it too */
for (i = 0; i < NUM_WEARS; i++)
if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_BOAT)
return (1);
// they can't swim here, so no need for a skill check.
if (SECT(going_to) == SECT_WATER_NOSWIM || SECT(going_to) == SECT_UD_WATER_NOSWIM || SECT(going_to) == SECT_UD_NOSWIM)
return 0;
/* we should do a swim check now */
int swim_dc = 13 + ZONE_MINLVL(GET_ROOM_ZONE(going_to));
send_to_char(ch, "Swim DC: %d - ", swim_dc);
if (!skill_check(ch, ABILITY_SWIM, swim_dc))
{
send_to_char(ch, "You attempt to swim, but fail!\r\n");
USE_MOVE_ACTION(ch);
return 0;
}
else
{ /*success!*/
send_to_char(ch, "You successfully swim!: ");
return 1;
}
return (0);
}
/* Simple function to determine if char can fly. */
int has_flight(struct char_data *ch)
{
struct obj_data *obj;
int i;
if (GET_LEVEL(ch) >= LVL_IMMORT)
return (1);
if (is_flying(ch))
return (1);
/* Non-wearable flying items in inventory will do it. */
for (obj = ch->carrying; obj; obj = obj->next_content)
if (OBJAFF_FLAGGED(obj, AFF_FLYING) && (find_eq_pos(ch, obj, NULL) < 0))
return (1);
/* Any equipped objects with AFF_FLYING will do it too. */
for (i = 0; i < NUM_WEARS; i++)
if (GET_EQ(ch, i) && OBJAFF_FLAGGED(GET_EQ(ch, i), AFF_FLYING))
return (1);
return (0);
}
/* Simple function to determine if char can scuba. */
int has_scuba(struct char_data *ch, room_rnum destination)
{
struct obj_data *obj;
int i;
if (GET_LEVEL(ch) >= LVL_IMMORT)
return (1);
if (AFF_FLAGGED(ch, AFF_SCUBA))
return (1);
/* Non-wearable scuba items in inventory will do it. */
for (obj = ch->carrying; obj; obj = obj->next_content)
if (OBJAFF_FLAGGED(obj, AFF_SCUBA) && (find_eq_pos(ch, obj, NULL) < 0))
return (1);
/* Any equipped objects with AFF_SCUBA will do it too. */
for (i = 0; i < NUM_WEARS; i++)
if (GET_EQ(ch, i) && OBJAFF_FLAGGED(GET_EQ(ch, i), AFF_SCUBA))
return (1);
if (IS_SET_AR(ROOM_FLAGS(destination), ROOM_AIRY))
return (1);
return (0);
}
/* Simple function to determine if char can climb */
int can_climb(struct char_data *ch)
{
struct obj_data *obj;
int i;
if (GET_LEVEL(ch) >= LVL_IMMORT)
return (1);
if (has_flight(ch))
return 1;
if (AFF_FLAGGED(ch, AFF_CLIMB))
return (1);
/* Non-wearable 'climb' items in inventory will do it. */
for (obj = ch->carrying; obj; obj = obj->next_content)
if (OBJAFF_FLAGGED(obj, AFF_CLIMB) && (find_eq_pos(ch, obj, NULL) < 0))
return (1);
/* Any equipped objects with AFF_CLIMB will do it too. */
for (i = 0; i < NUM_WEARS; i++)
if (GET_EQ(ch, i) && OBJAFF_FLAGGED(GET_EQ(ch, i), AFF_CLIMB))
return (1);
return (0);
}
/** Leave tracks in the current room
* */
#define TRACKS_UNDEFINED 0
#define TRACKS_IN 1
#define TRACKS_OUT 2
#define DIR_NONE -1
void create_tracks(struct char_data *ch, int dir, int flag)
{
struct room_data *room = NULL;
struct trail_data *cur = NULL;
struct trail_data *prev = NULL;
struct trail_data *new_trail = NULL;
if (IN_ROOM(ch) != NOWHERE)
{
room = &world[ch->in_room];
}
else
{
log("SYSERR: Char at location NOWHERE trying to create tracks.");
return;
}
/*
Here we create the track structure, set the values and assign it to the room.
At the same time, we can prune off any really old trails. Threshold is set,
in seconds, in trails.h. Eventually this cna be adjusted based on weather -
rain/show/wind can all obscure trails.
*/
CREATE(new_trail, struct trail_data, 1);
new_trail->name = strdup(GET_NAME(ch));
new_trail->race = (IS_NPC(ch) ? strdup(race_family_types[GET_NPC_RACE(ch)]) : strdup(race_list[GET_RACE(ch)].name));
new_trail->from = (flag == TRACKS_IN ? dir : DIR_NONE);
new_trail->to = (flag == TRACKS_OUT ? dir : DIR_NONE);
new_trail->age = time(NULL);
new_trail->next = room->trail_tracks->head;
new_trail->prev = NULL;
if (new_trail->next != NULL)
{
room->trail_tracks->head->prev = new_trail;
}
room->trail_tracks->head = new_trail;
prev = NULL;
for (cur = room->trail_tracks->head; cur != NULL; cur = cur->next)
{
if (time(NULL) - cur->age >= TRAIL_PRUNING_THRESHOLD)
{
if (prev != NULL)
{
// if (prev->next != NULL) DISPOSE(prev->next);
prev->next = cur->next;
if (cur->next != NULL)
{
cur->next->prev = prev;
}
}
else
{
room->trail_tracks->head = cur->next;
if (cur->next != NULL)
{
// if (cur->next->prev != NULL) DISPOSE(cur->next->prev);
cur->next->prev = NULL;
}
}
}
prev = cur;
}
/*
struct trail_data_list *trail_scent;
struct trail_data_list *trail_blood;
*/
}
/** Move a PC/NPC character from their current location to a new location. This
* is the standard movement locomotion function that all normal walking
* movement by characters should be sent through. This function also defines
* the move cost of normal locomotion as:
* ( (move cost for source room) + (move cost for destination) ) / 2
*
* @pre Function assumes that ch has no master controlling character, that
* ch has no followers (in other words followers won't be moved by this
* function) and that the direction traveled in is one of the valid, enumerated
* direction.
* @param ch The character structure to attempt to move.
* @param dir The defined direction (NORTH, SOUTH, etc...) to attempt to
* move into.
* @param need_specials_check If TRUE will cause
* @retval int 1 for a successful move (ch is now in a new location)
* or 0 for a failed move (ch is still in the original location). */
int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
{
/* Begin Local variable definitions */
/*---------------------------------------------------------------------*/
/* Used in our special proc check. By default, we pass a NULL argument
* when checking for specials */
char spec_proc_args[MAX_INPUT_LENGTH] = {'\0'};
/* The room the character is currently in and will move from... */
room_rnum was_in = NOWHERE;
/* ... and the room the character will move into. */
room_rnum going_to = NOWHERE;
/* How many movement points are required to travel from was_in to going_to.
* We redefine this later when we need it. */
int need_movement = 0;
/* used for looping the room for sneak-checks */
struct char_data *tch = NULL, *next_tch = NULL;
// for mount code
int same_room = 0, riding = 0, ridden_by = 0;
/* extra buffers */
char buf2[MAX_STRING_LENGTH] = {'\0'};
// char buf3[MAX_STRING_LENGTH] = {'\0'};
/* singlefile variables */
struct char_data *other;
struct char_data **prev;
bool was_top = TRUE;
/* Wilderness variables */
int new_x = 0, new_y = 0;
/* added some dummy checks to deal with a fairly mysterious crash */
if (!ch)
return 0;
if (IN_ROOM(ch) == NOWHERE)
return 0;
if (dir < 0 || dir >= NUM_OF_DIRS)
return 0;
/* dummy check, if you teleport while in a falling event, BOOM otherwise :P */
if (!EXIT(ch, dir))
return 0;
/* The following is to support the wilderness code. */
if (ZONE_FLAGGED(GET_ROOM_ZONE(IN_ROOM(ch)), ZONE_WILDERNESS) && (EXIT(ch, dir)->to_room == real_room(1000000)))
{
new_x = X_LOC(ch); // world[IN_ROOM(ch)].coords[0];
new_y = Y_LOC(ch); // world[IN_ROOM(ch)].coords[1];
/* This is a wilderness movement! Find out which coordinates we need
* to check, based on the dir and local coordinates. */
switch (dir)
{
case NORTH:
new_y++;
break;
case SOUTH:
new_y--;
break;
case EAST:
new_x++;
break;
case WEST:
new_x--;
break;
default:
/* Bad direction for wilderness travel.*/
return 0;
}
going_to = find_room_by_coordinates(new_x, new_y);
if (going_to == NOWHERE)
{
going_to = find_available_wilderness_room();
if (going_to == NOWHERE)
{
log("SYSERR: Wilderness movement failed from (%d, %d) to (%d, %d)", X_LOC(ch), Y_LOC(ch), new_x, new_y);
return 0;
}
/* Must set the coords, etc in the going_to room. */
assign_wilderness_room(going_to, new_x, new_y);
}
}
else if (world[IN_ROOM(ch)].dir_option[dir])
{
going_to = EXIT(ch, dir)->to_room;
/* Since we are in non-wilderness moving to wilderness, set up the coords. */
if (ZONE_FLAGGED(GET_ROOM_ZONE(going_to), ZONE_WILDERNESS))
{
new_x = world[going_to].coords[0];
new_y = world[going_to].coords[1];
}
}
if (going_to == NOWHERE)
return 0;
was_in = IN_ROOM(ch);
/* end dummy checks */
/*---------------------------------------------------------------------*/
/* End Local variable definitions */
/* Begin checks that can prevent a character from leaving the was_in room. */
/* Future checks should be implemented within this section and return 0. */
/*---------------------------------------------------------------------*/
/* Check for special routines that might activate because of the move and
* also might prevent the movement. Special requires commands, so we pass
* in the "command" equivalent of the direction (ie. North is '1' in the
* command list, but NORTH is defined as '0').
* Note -- only check if following; this avoids 'double spec-proc' bug */
if (need_specials_check && special(ch, dir + 1, spec_proc_args))
return 0;
/* Leave Trigger Checks: Does a leave trigger block exit from the room? */
/* next 3 if blocks prevent teleport crashes */
if (!leave_mtrigger(ch, dir) || IN_ROOM(ch) != was_in)
return 0;
if (!leave_wtrigger(&world[IN_ROOM(ch)], ch, dir) || IN_ROOM(ch) != was_in)
return 0;
if (!leave_otrigger(&world[IN_ROOM(ch)], ch, dir) || IN_ROOM(ch) != was_in)
return 0;
if (AFF_FLAGGED(ch, AFF_GRAPPLED) || AFF_FLAGGED(ch, AFF_ENTANGLED))
{
send_to_char(ch, "You struggle to move but you are unable to leave the area!\r\n");
act("$n struggles to move, but can't!", FALSE, ch, 0, 0, TO_ROOM);
return 0;
}
if (affected_by_spell(ch, SKILL_DEFENSIVE_STANCE) &&
!HAS_FEAT(ch, FEAT_MOBILE_DEFENSE))
{
send_to_char(ch, "You can't move while in defensive stance!\r\n");
return 0;
}
/* check if they're mounted */
if (RIDING(ch))
riding = 1;
if (RIDDEN_BY(ch))
ridden_by = 1;
/* if they're mounted, are they in the same room w/ their mount(ee)? */
if (riding && RIDING(ch)->in_room == ch->in_room)
same_room = 1;
else if (ridden_by && RIDDEN_BY(ch)->in_room == ch->in_room)
same_room = 1;
/* tamed mobiles cannot move about */
if (ridden_by && same_room && AFF_FLAGGED(ch, AFF_TAMED))
{
send_to_char(ch, "You've been tamed. Now act it!\r\n");
return 0;
}
/* begin singlefile mechanic */
if (ROOM_FLAGGED(ch->in_room, ROOM_SINGLEFILE))
{
other = get_char_ahead_of_me(ch, dir);
if (other && RIDING(other) != ch && RIDDEN_BY(other) != ch)
{
if (GET_POS(other) == POS_RECLINING)
{
was_top = is_top_of_room_for_singlefile(ch, dir);
prev = &world[ch->in_room].people;
while (*prev)
{
if (*prev == ch)
{
*prev = ch->next_in_room;
ch->next_in_room = 0;
}
if (*prev)
prev = &((*prev)->next_in_room);
}
prev = &world[ch->in_room].people;
if (was_top)
{
while (*prev)
{
if (*prev == other)
{
*prev = ch;
ch->next_in_room = other;
}
prev = &((*prev)->next_in_room);
}
}
else
{
ch->next_in_room = other->next_in_room;
other->next_in_room = ch;
}
act("You squeeze by the prone body of $N.", FALSE, ch, 0, other, TO_CHAR);
act("$n squeezes by YOU.", FALSE, ch, 0, other, TO_VICT);
act("$n squeezes by the prone body of $N.", FALSE, ch, 0, other, TO_NOTVICT);
return 0;
}
else if (GET_POS(ch) == POS_RECLINING && GET_POS(other) >= POS_FIGHTING && FIGHTING(ch) != other && FIGHTING(other) != ch)
{
was_top = is_top_of_room_for_singlefile(ch, dir);
prev = &world[ch->in_room].people;
while (*prev)
{
if (*prev == ch)
{
*prev = ch->next_in_room;
ch->next_in_room = 0;
}
if (*prev)
prev = &((*prev)->next_in_room);
}
prev = &world[ch->in_room].people;
if (was_top)
{
while (*prev)
{
if (*prev == other)
{
*prev = ch;
ch->next_in_room = other;
}
prev = &((*prev)->next_in_room);
}
}
else
{
ch->next_in_room = other->next_in_room;
other->next_in_room = ch;
}
act("You crawl by $N.", FALSE, ch, 0, other, TO_CHAR);
act("$n crawls by YOU.", FALSE, ch, 0, other, TO_VICT);
act("$n crawls by $N.", FALSE, ch, 0, other, TO_NOTVICT);
return 0;
}
else
{
act("You bump into $N.", FALSE, ch, 0, other, TO_CHAR);
return 0;
}
}
}
/* end singlefile mechanic */
/* Charm effect: Does it override the movement?
for now it is cut out of the code */
// dummy check
if (going_to == NOWHERE)
return 0;
/* druid spell */
if (IS_EVIL(ch) && IS_HOLY(going_to))
{
send_to_char(ch, "The sanctity of the area prevents "
"you from entering.\r\n");
return 0;
}
if (IS_GOOD(ch) && IS_UNHOLY(going_to))
{
send_to_char(ch, "The corruption of the area prevents "
"you from entering.\r\n");
return 0;
}
/* Water, No Swimming Rooms: Does the deep water prevent movement? */
if ((SECT(was_in) == SECT_WATER_NOSWIM) || (SECT(was_in) == SECT_UD_NOSWIM) ||
(SECT(going_to) == SECT_WATER_NOSWIM) || (SECT(going_to) == SECT_UD_NOSWIM))
{
if ((riding && !has_boat(RIDING(ch), going_to)) || !has_boat(ch, going_to))
{
send_to_char(ch, "You need a boat to go there.\r\n");
return (0);
}
}
if (SECT(was_in) == SECT_WATER_SWIM || SECT(was_in) == SECT_UD_WATER || SECT(was_in) == SECT_UNDERWATER ||
SECT(going_to) == SECT_WATER_SWIM || SECT(going_to) == SECT_UD_WATER || SECT(going_to) == SECT_UNDERWATER)
{
if ((riding && !has_boat(RIDING(ch), going_to)) || !has_boat(ch, going_to))
{
if (GET_MOVE(ch) < 20)
{
send_to_char(ch, "You don't have the energy to try and swim there.\r\n");
return 0;
}
send_to_char(ch, "You try to swim there, but fail.\r\n");
GET_MOVE(ch) -= 20;
return (0);
}
}
/* Flying Required: Does lack of flying prevent movement? */
if ((SECT(was_in) == SECT_FLYING) || (SECT(going_to) == SECT_FLYING) ||
(SECT(was_in) == SECT_UD_NOGROUND) || (SECT(going_to) == SECT_UD_NOGROUND))
{
if (char_has_mud_event(ch, eFALLING))
; /* zusuk's cheesy falling code */
else if ((riding && !has_flight(RIDING(ch))) || !has_flight(ch))
{
send_to_char(ch, "You need to be flying to go there!\r\n");
return (0);
}
}
/* Underwater Room: Does lack of underwater breathing prevent movement? */
if ((SECT(was_in) == SECT_UNDERWATER) ||
(SECT(going_to) == SECT_UNDERWATER))
{
if (!has_scuba(ch, going_to) && (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)))
{
send_to_char(ch,
"You need to be able to breathe water to go there!\r\n");
return (0);
}
}
/* High Mountain (and any other climb rooms) */
if ((SECT(was_in) == SECT_HIGH_MOUNTAIN) ||
(SECT(going_to) == SECT_HIGH_MOUNTAIN))
{
if ((riding && !can_climb(RIDING(ch))) || !can_climb(ch))
{
send_to_char(ch, "You need to be able to climb to go there!\r\n");
return (0);
}
}
/* Ocean restriction */
if (SECT(was_in) == SECT_OCEAN || SECT(going_to) == SECT_OCEAN)
{
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE))
{
send_to_char(ch, "You need an ocean-ready ship to go there!\r\n");
return (0);
}
}
/* flight restricted to enter that room */
if (ROOM_FLAGGED(going_to, ROOM_NOFLY) && is_flying(ch))
{
send_to_char(ch, "It is not possible to fly in that direction\r\n");
return 0;
}
/* size restricted to enter that room */
if (ROOM_FLAGGED(going_to, ROOM_SIZE_TINY) && GET_SIZE(ch) > SIZE_TINY)
{
send_to_char(ch, "You'd have to be tiny or smaller to go there.\r\n");
return 0;
}
if (ROOM_FLAGGED(going_to, ROOM_SIZE_DIMINUTIVE) && GET_SIZE(ch) > SIZE_DIMINUTIVE)
{
send_to_char(ch, "You'd have to be diminutive or smaller to go there.\r\n");
return 0;
}
/* Houses: Can the player walk into the house? */
if (ROOM_FLAGGED(was_in, ROOM_ATRIUM))
{
if (!House_can_enter(ch, GET_ROOM_VNUM(going_to)))
{
send_to_char(ch, "That's private property -- no trespassing!\r\n");
return (0);
}
}
/* Check zone flag restrictions */
if (ZONE_FLAGGED(GET_ROOM_ZONE(going_to), ZONE_CLOSED))
{
send_to_char(ch, "A mysterious barrier forces you back! That area is "
"off-limits.\r\n");
return (0);
}
if (ZONE_FLAGGED(GET_ROOM_ZONE(going_to), ZONE_NOIMMORT) &&
(GET_LEVEL(ch) >= LVL_IMMORT) && (GET_LEVEL(ch) < LVL_GRSTAFF))
{
send_to_char(ch, "A mysterious barrier forces you back! That area is off-limits.\r\n");
return (0);
}
/* Room Size Capacity: Is the room full of people already? */
if (riding && ROOM_FLAGGED(going_to, ROOM_TUNNEL))
{
send_to_char(ch, "There isn't enough space to enter mounted!\r\n");
return 0;
}