Mount update 1

This commit is contained in:
kinther 2025-12-30 13:31:56 -08:00
parent dedc5b7def
commit b6bbe522bb
11 changed files with 359 additions and 32 deletions

View file

@ -60,7 +60,9 @@ Changes in v1.1.0-alpha:
* "audit ac" command for immortals (formerly "acaudit"), allowing for further audit commands in the future
* Minor score output change to only show quest status while on a quest, PC/NPC name, sdesc, and current ldesc
* Added ability to reroll initial stats if they are not to player's liking, and undo reroll if needed
* Removed alignment from game - no more GOOD/EVIL flags and restrictions on items/shops
* Removed alignment from game - no more GOOD/EVIL flags or restrictions on shops
* Removed ANTI_ flags related to class restrictions on what objects they can use
* Mounts added to help with long trips, and ability to use them as pack animals
Features to be implemented in the next few releases:
@ -68,7 +70,6 @@ Features to be implemented in the next few releases:
* SECTOR/ROOM type changes to make terrain movement easier or more difficult
* Subclass selection to personalize character further
* Combat is slowed down so it isn't over in < 15 seconds (unless you're far outmatched)
* Mounts added to help with long trips
* Wagons added to help with caravans
* BUILDING object type created to allow enter/leave
* Updated BUILDING object type so that it can be damaged and no longer enterable (but someone can leave at cost to health)

View file

@ -20,6 +20,7 @@ B
~
6218 0 0 0 0 0 0 0 0 E
1 3d20+40
@ -38,17 +39,17 @@ Skill 145 5
Skill 146 5
Skill 147 5
E
L 3 118 1
L 5 131 1
L 6 110 1
L 7 108 1
L 8 115 1
L 9 124 1
L 10 107 1
L 11 111 1
L 15 117 1
L 16 117 1
L 17 127 1
L 16 117 1
L 15 117 1
L 11 111 1
L 10 107 1
L 9 124 1
L 8 115 1
L 7 108 1
L 6 110 1
L 5 131 1
L 3 118 1
#101
Sally~
slim lanky human soldier guard~
@ -72,6 +73,7 @@ B
~
6218 0 0 0 0 0 0 0 0 E
1 3d20+40
@ -92,17 +94,17 @@ Skill 152 5
Skill 156 5
Skill 163 5
E
L 17 127 1
L 16 117 1
L 15 117 1
L 11 111 1
L 10 107 1
L 9 124 1
L 8 115 1
L 7 108 1
L 6 110 1
L 5 131 1
L 3 118 1
L 5 131 1
L 6 110 1
L 7 108 1
L 8 115 1
L 9 124 1
L 10 107 1
L 11 111 1
L 15 117 1
L 16 117 1
L 17 127 1
#102
Baldy~
barkeep stocky bald~
@ -126,13 +128,14 @@ B
~
10 0 0 0 0 0 0 0 0 E
1 3d12+60
8 8 1
E
L 14 113 1
L 9 112 1
L 14 113 1
#103
Lanky~
woman lanky scarred~
@ -157,6 +160,7 @@ B
~
10 0 0 0 0 0 0 0 0 E
1 3d8+60
@ -191,6 +195,7 @@ B
It's a rat.
~
8 0 0 0 0 0 0 0 0 E
@ -206,4 +211,27 @@ Species: 25
Age: 42
AtkT 4
E
#105
Kank~
kank sandy brown~
a sandy brown kank~
A sandy brown kank is here, clacking its pincers.
~
It looks unfinished.
~
B
No background has been recorded.
~
1048584 0 0 0 0 0 0 0 0 E
0 0d0+10
8 8 0
Str: 18
Dex: 5
Int: 2
Wis: 7
Con: 18
Cha: 1
Species: 21
AtkT 4
E
$

View file

@ -176,8 +176,10 @@ ACMD(do_gen_door);
ACMD(do_enter);
ACMD(do_follow);
ACMD(do_leave);
ACMD(do_mount);
ACMD(do_move);
ACMD(do_rest);
ACMD(do_dismount);
ACMD(do_sit);
ACMD(do_sleep);
ACMD(do_stand);

View file

@ -569,6 +569,43 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
CCNRM(ch, C_NRM));
}
if (AFF_FLAGGED(i, AFF_MOUNTED) && MOUNT(i) &&
IN_ROOM(MOUNT(i)) == IN_ROOM(i) &&
MOB_FLAGGED(MOUNT(i), MOB_MOUNT)) {
const char *rdesc = get_char_sdesc(i);
const char *mdesc = get_char_sdesc(MOUNT(i));
if (rdesc && *rdesc)
send_to_char(ch, "%c%s", UPPER(*rdesc), rdesc + 1);
else
send_to_char(ch, "Someone");
if (mdesc && *mdesc)
send_to_char(ch, " is riding %s here.", mdesc);
else
send_to_char(ch, " is riding someone here.");
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, " (invisible)");
if (AFF_FLAGGED(i, AFF_HIDE))
send_to_char(ch, " (hidden)");
if (!IS_NPC(i) && !i->desc)
send_to_char(ch, " (linkless)");
if (!IS_NPC(i) && PLR_FLAGGED(i, PLR_WRITING))
send_to_char(ch, " (writing)");
if (!IS_NPC(i) && PRF_FLAGGED(i, PRF_BUILDWALK))
send_to_char(ch, " (buildwalk)");
if (!IS_NPC(i) && PRF_FLAGGED(i, PRF_AFK))
send_to_char(ch, " (AFK)");
send_to_char(ch, "\r\n");
if (AFF_FLAGGED(i, AFF_SANCTUARY))
act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
return;
}
/* Custom ldesc overrides position-based output. */
if (i->char_specials.custom_ldesc && i->player.long_descr) {
if (AFF_FLAGGED(i, AFF_INVISIBLE))
@ -692,6 +729,23 @@ static void build_current_ldesc(const struct char_data *ch, char *out, size_t ou
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) &&
IN_ROOM(MOUNT(ch)) == IN_ROOM(ch) &&
MOB_FLAGGED(MOUNT(ch), MOB_MOUNT)) {
const char *rdesc = get_char_sdesc(ch);
const char *mdesc = get_char_sdesc(MOUNT(ch));
if (!rdesc || !*rdesc)
rdesc = "someone";
if (!mdesc || !*mdesc)
mdesc = "someone";
snprintf(out, outsz, "%c%s is riding %s here.",
UPPER(*rdesc), rdesc + 1, mdesc);
strip_trailing_crlf(out);
return;
}
if (ch->char_specials.custom_ldesc && ch->player.long_descr) {
strlcpy(out, ch->player.long_descr, outsz);
strip_trailing_crlf(out);
@ -746,6 +800,9 @@ static void list_char_to_char(struct char_data *list, struct char_data *ch)
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_HOLYLIGHT) &&
IS_NPC(i) && i->player.long_descr && *i->player.long_descr == '.')
continue;
if (RIDDEN_BY(i) && IN_ROOM(RIDDEN_BY(i)) == IN_ROOM(i) &&
CAN_SEE(ch, RIDDEN_BY(i)))
continue;
send_to_char(ch, "%s", CCYEL(ch, C_NRM));
if (CAN_SEE(ch, i))
list_one_char(i, ch);
@ -972,6 +1029,9 @@ static bool look_list_direction_chars(struct char_data *ch, room_rnum room)
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_HOLYLIGHT) &&
IS_NPC(tch) && tch->player.long_descr && *tch->player.long_descr == '.')
continue;
if (RIDDEN_BY(tch) && IN_ROOM(RIDDEN_BY(tch)) == room &&
CAN_SEE(ch, RIDDEN_BY(tch)))
continue;
if (AFF_FLAGGED(tch, AFF_HIDE)) {
if (CAN_SEE(ch, tch) || look_can_spot_hidden(ch, tch, room)) {
@ -1135,6 +1195,42 @@ static void look_at_target(struct char_data *ch, char *arg)
return;
}
if (!found_char) {
struct char_data *tch;
char tmp[MAX_INPUT_LENGTH];
char *name = tmp;
int matchnum;
strlcpy(tmp, arg, sizeof(tmp));
matchnum = get_number(&name);
if (matchnum > 0) {
for (tch = world[IN_ROOM(ch)].people; tch && matchnum; tch = tch->next_in_room) {
if (tch == ch)
continue;
if (!AFF_FLAGGED(tch, AFF_MOUNTED))
continue;
if (!CAN_SEE(ch, tch))
continue;
if (isname(name, (char *)get_char_sdesc(tch))) {
if (--matchnum == 0) {
found_char = tch;
break;
}
}
}
}
}
if (found_char != NULL) {
look_at_char(found_char, ch);
if (ch != found_char) {
if (CAN_SEE(found_char, ch))
act("$n looks at you.", TRUE, ch, 0, found_char, TO_VICT);
act("$n looks at $N.", TRUE, ch, 0, found_char, TO_NOTVICT);
}
return;
}
/* Strip off "number." from 2.foo and friends. */
if (!(fnum = get_number(&arg))) {
send_to_char(ch, "Look at what?\r\n");
@ -1436,7 +1532,13 @@ ACMD(do_score)
FIGHTING(ch) ? PERS(FIGHTING(ch), ch) : "thin air");
break;
case POS_STANDING:
send_to_char(ch, "You are standing.\r\n");
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) &&
IN_ROOM(MOUNT(ch)) == IN_ROOM(ch) &&
MOB_FLAGGED(MOUNT(ch), MOB_MOUNT)) {
send_to_char(ch, "You are riding %s.\r\n", get_char_sdesc(MOUNT(ch)));
} else {
send_to_char(ch, "You are standing.\r\n");
}
break;
default:
send_to_char(ch, "You are floating.\r\n");

View file

@ -44,6 +44,9 @@ static bool validate_furniture_use(struct char_data *ch, struct obj_data *furnit
bool already_there);
static void attach_char_to_furniture(struct char_data *ch, struct obj_data *furniture);
static const char *position_gerund(int pos);
static void clear_mount_state(struct char_data *ch);
static bool mount_skill_check(struct char_data *ch, int dc);
static bool resolve_mounted_move(struct char_data *ch, struct char_data **mount);
/* simple function to determine if char can walk on water */
@ -263,6 +266,67 @@ static const char *position_gerund(int pos)
return "using";
}
}
static void clear_mount_state(struct char_data *ch)
{
struct char_data *mount;
if (!ch)
return;
mount = MOUNT(ch);
if (mount && RIDDEN_BY(mount) == ch)
RIDDEN_BY(mount) = NULL;
MOUNT(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
}
static bool mount_skill_check(struct char_data *ch, int dc)
{
int total = roll_skill_check(ch, SKILL_ANIMAL_HANDLING, 0, NULL);
bool success = (total >= dc);
gain_skill(ch, "animal handling", success);
return success;
}
static bool resolve_mounted_move(struct char_data *ch, struct char_data **mount)
{
struct char_data *mount_ch;
if (!AFF_FLAGGED(ch, AFF_MOUNTED))
return FALSE;
mount_ch = MOUNT(ch);
if (!mount_ch || IN_ROOM(mount_ch) != IN_ROOM(ch)) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return FALSE;
}
if (!mount_skill_check(ch, 5)) {
send_to_char(ch, "%s refuses to move.\r\n", get_char_sdesc(mount_ch));
act("$n tries to urge $N forward, but $N refuses to move.",
TRUE, ch, 0, mount_ch, TO_ROOM);
return FALSE;
}
if (!mount_skill_check(ch, 3)) {
int dam = dice(1, 8);
send_to_char(ch, "You are thrown from %s!\r\n", get_char_sdesc(mount_ch));
act("$n is thrown from $N!", TRUE, ch, 0, mount_ch, TO_ROOM);
clear_mount_state(ch);
GET_POS(ch) = POS_RESTING;
WAIT_STATE(ch, PULSE_VIOLENCE);
damage(ch, ch, dam, TYPE_SUFFERING);
return FALSE;
}
if (mount)
*mount = mount_ch;
return TRUE;
}
/** 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
@ -293,6 +357,9 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
/* How many stamina points are required to travel from was_in to going_to.
* We redefine this later when we need it. */
int need_movement = 0;
/* Character whose stamina is used for movement (mounts override). */
struct char_data *stamina_ch = ch;
bool mounted_move = FALSE;
/* Contains the "leave" message to display to the was_in room. */
char leave_message[SMALL_BUFSIZE];
/*---------------------------------------------------------------------*/
@ -406,10 +473,18 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
movement_loss[SECT(going_to)]) / 2;
/* Move Point Requirement Check */
if (GET_STAMINA(ch) < need_movement && !IS_NPC(ch))
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) && IN_ROOM(MOUNT(ch)) == was_in)
{
stamina_ch = MOUNT(ch);
mounted_move = TRUE;
}
if (GET_STAMINA(stamina_ch) < need_movement && (mounted_move || !IS_NPC(ch)))
{
if (need_specials_check && ch->master)
send_to_char(ch, "You are too exhausted to follow.\r\n");
else if (mounted_move)
send_to_char(ch, "Your mount is too exhausted.\r\n");
else
send_to_char(ch, "You are too exhausted.\r\n");
@ -423,8 +498,8 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
/* Begin: the leave operation. */
/*---------------------------------------------------------------------*/
/* If applicable, subtract movement cost. */
if (GET_LEVEL(ch) < LVL_IMMORT && !IS_NPC(ch))
GET_STAMINA(ch) -= need_movement;
if (GET_LEVEL(ch) < LVL_IMMORT && (mounted_move || !IS_NPC(ch)))
GET_STAMINA(stamina_ch) -= need_movement;
/* Generate the leave message and display to others in the was_in room. */
if (AFF_FLAGGED(ch, AFF_SNEAK)) {
@ -500,6 +575,7 @@ int perform_move(struct char_data *ch, int dir, int need_specials_check)
{
room_rnum was_in;
struct follow_type *k, *next;
struct char_data *mount = NULL;
if (ch == NULL || dir < 0 || dir >= NUM_OF_DIRS || FIGHTING(ch))
return (0);
@ -513,13 +589,22 @@ int perform_move(struct char_data *ch, int dir, int need_specials_check)
else
send_to_char(ch, "It seems to be closed.\r\n");
} else {
if (!ch->followers)
return (do_simple_move(ch, dir, need_specials_check));
was_in = IN_ROOM(ch);
if (AFF_FLAGGED(ch, AFF_MOUNTED) &&
!resolve_mounted_move(ch, &mount))
return (0);
if (!do_simple_move(ch, dir, need_specials_check))
return (0);
if (mount && IN_ROOM(mount) == was_in) {
char_from_room(mount);
char_to_room(mount, IN_ROOM(ch));
}
if (!ch->followers)
return (1);
for (k = ch->followers; k; k = next) {
next = k->next;
if ((IN_ROOM(k->follower) == was_in) &&
@ -941,6 +1026,11 @@ ACMD(do_stand)
int ordinal = 0;
int orig_pos = GET_POS(ch);
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Stand"))
return;
@ -1067,6 +1157,11 @@ ACMD(do_sit)
int ordinal = 0;
int orig_pos = GET_POS(ch);
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Sit"))
return;
@ -1175,6 +1270,11 @@ ACMD(do_rest)
bool has_target = FALSE, used_number = FALSE;
int ordinal = 0;
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Rest"))
return;
@ -1303,6 +1403,11 @@ ACMD(do_sleep)
bool has_target = FALSE, used_number = FALSE;
int ordinal = 0;
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Sleep"))
return;
@ -1484,6 +1589,66 @@ ACMD(do_follow)
}
}
ACMD(do_mount)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
one_argument(argument, arg);
if (!*arg) {
send_to_char(ch, "Mount what?\r\n");
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You are already mounted.\r\n");
return;
}
if (!(mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM))) {
send_to_char(ch, "%s", CONFIG_NOPERSON);
return;
}
if (mount == ch) {
send_to_char(ch, "You can't mount yourself.\r\n");
return;
}
if (!IS_NPC(mount) || !MOB_FLAGGED(mount, MOB_MOUNT)) {
send_to_char(ch, "You can't mount %s!\r\n", get_char_sdesc(mount));
return;
}
if (RIDDEN_BY(mount)) {
act("$N is already being ridden.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
SET_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
MOUNT(ch) = mount;
RIDDEN_BY(mount) = ch;
act("You mount $N.", FALSE, ch, 0, mount, TO_CHAR);
act("$n mounts $N.", TRUE, ch, 0, mount, TO_ROOM);
}
ACMD(do_dismount)
{
struct char_data *mount = MOUNT(ch);
if (!AFF_FLAGGED(ch, AFF_MOUNTED) || !mount) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return;
}
act("You dismount $N.", FALSE, ch, 0, mount, TO_CHAR);
act("$n dismounts $N.", TRUE, ch, 0, mount, TO_ROOM);
clear_mount_state(ch);
}
ACMD(do_unfollow)
{
if (ch->master) {

View file

@ -240,6 +240,7 @@ const char *action_bits[] = {
"NO_BLIND",
"NO_KILL",
"DEAD", /* You should never see this. */
"MOUNT",
"\n"
};
@ -311,6 +312,7 @@ const char *affected_bits[] =
"CHARM",
"BANDAGED",
"LISTEN",
"MOUNTED",
"\n"
};

View file

@ -1003,6 +1003,22 @@ void extract_char_final(struct char_data *ch)
}
}
if (AFF_FLAGGED(ch, AFF_MOUNTED) || MOUNT(ch)) {
struct char_data *mount = MOUNT(ch);
if (mount && RIDDEN_BY(mount) == ch)
RIDDEN_BY(mount) = NULL;
MOUNT(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
}
if (RIDDEN_BY(ch)) {
struct char_data *rider = RIDDEN_BY(ch);
if (rider && MOUNT(rider) == ch) {
MOUNT(rider) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(rider), AFF_MOUNTED);
}
RIDDEN_BY(ch) = NULL;
}
/* On with the character's assets... */
if (ch->followers || ch->master)
die_follower(ch);

View file

@ -137,6 +137,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "detach" , "detach" , POS_DEAD , do_detach , LVL_BUILDER, 0 },
{ "diagnose" , "diag" , POS_RESTING , do_diagnose , 0, 0 },
{ "dig" , "dig" , POS_DEAD , do_dig , LVL_BUILDER, 0 },
{ "dismount" , "dism" , POS_STANDING, do_dismount , 0, 0 },
{ "display" , "disp" , POS_DEAD , do_display , 0, 0 },
{ "drink" , "dri" , POS_RESTING , do_drink , 0, SCMD_DRINK },
{ "drop" , "dro" , POS_RESTING , do_drop , 0, SCMD_DROP },
@ -198,6 +199,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "last" , "last" , POS_DEAD , do_last , LVL_GOD, 0 },
{ "leave" , "lea" , POS_STANDING, do_leave , 0, 0 },
{ "list" , "lis" , POS_STANDING, do_not_here , 0, 0 },
{ "mount" , "mou" , POS_STANDING, do_mount , 0, 0 },
{ "listen" , "lisn" , POS_RESTING , do_listen , 0, 0 },
{ "links" , "lin" , POS_STANDING, do_links , LVL_GOD, 0 },
{ "lock" , "loc" , POS_SITTING , do_gen_door , 0, SCMD_LOCK },

View file

@ -81,6 +81,7 @@ void mobile_activity(void)
((door = rand_number(0, 18)) < DIR_COUNT) && CAN_GO(ch, door) &&
!ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_NOMOB) &&
!ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_DEATH) &&
!RIDDEN_BY(ch) &&
(!MOB_FLAGGED(ch, MOB_STAY_ZONE) ||
(world[EXIT(ch, door)->to_room].zone == world[IN_ROOM(ch)].zone)))
{

View file

@ -267,8 +267,9 @@
#define MOB_NOBLIND 17 /**< Mob can't be blinded */
#define MOB_NOKILL 18 /**< Mob can't be attacked */
#define MOB_NOTDEADYET 19 /**< (R) Mob being extracted */
#define MOB_MOUNT 20 /**< Mob can be mounted */
#define NUM_MOB_FLAGS 19
#define NUM_MOB_FLAGS 21
/* Preference flags: used by char_data.player_specials.pref */
#define PRF_BRIEF 0 /**< Room descs won't normally be shown */
@ -333,8 +334,9 @@
#define AFF_CHARM 22 /**< Char is charmed */
#define AFF_BANDAGED 23 /**< Character was bandaged recently */
#define AFF_LISTEN 24 /**< Actively eavesdropping */
#define AFF_MOUNTED 25 /**< Riding a mount */
/** Total number of affect flags */
#define NUM_AFF_FLAGS 25
#define NUM_AFF_FLAGS 26
/* Modes of connectedness: used by descriptor_data.state */
#define CON_PLAYING 0 /**< Playing - Nominal state */
@ -1005,6 +1007,8 @@ struct char_special_data
struct char_data *fighting; /**< Target of fight; else NULL */
struct char_data *hunting; /**< Target of NPC hunt; else NULL */
struct obj_data *furniture; /**< Object being sat on/in; else NULL */
struct char_data *mount; /**< Mount being ridden; else NULL */
struct char_data *rider; /**< Rider, if being mounted; else NULL */
struct char_data *next_in_furniture; /**< Next person sitting, else NULL */
byte position; /**< Standing, fighting, sleeping, etc. */

View file

@ -196,6 +196,10 @@ void advance_level(struct char_data *ch);
void char_from_furniture(struct char_data *ch);
/** What ch is currently sitting on. */
#define SITTING(ch) ((ch)->char_specials.furniture)
/** Mount ch is currently riding. */
#define MOUNT(ch) ((ch)->char_specials.mount)
/** Rider currently mounted on ch. */
#define RIDDEN_BY(ch) ((ch)->char_specials.rider)
/** Who is sitting next to ch, if anyone. */
#define NEXT_SITTING(ch) ((ch)->char_specials.next_in_furniture)
/** Who is sitting on this obj */