diff --git a/src/act.drive.cpp b/src/act.drive.cpp index 1ecd87628..184db6c7f 100644 --- a/src/act.drive.cpp +++ b/src/act.drive.cpp @@ -402,21 +402,15 @@ void do_raw_ram(struct char_data *ch, struct veh_data *veh, struct veh_data *tve return; } - // Alarm all NPCs inside the ramming vehicle. + // Alarm all NPCs inside the ramming vehicle. They don't get mad at the PC in particular, just mad in general. for (struct char_data *npc = veh->people; npc; npc = npc->next_in_veh) { - if (IS_NPC(npc)) { - GET_MOBALERT(npc) = MALERT_ALARM; - GET_MOBALERTTIME(npc) = 30; - } + set_mob_alarm(npc, NULL, 30); } if (tveh) { // Alarm all NPCs inside the targeted vehicle. for (struct char_data *npc = tveh->people; npc; npc = npc->next_in_veh) { - if (IS_NPC(npc)) { - GET_MOBALERT(npc) = MALERT_ALARM; - GET_MOBALERTTIME(npc) = 30; - } + set_mob_alarm(npc, ch, 30); } target = get_vehicle_modifier(veh) + veh->handling + modify_target(ch); diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 8be3885c5..78942025a 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -51,6 +51,7 @@ #include "zoomies.hpp" #include "vehicles.hpp" #include "moderation.hpp" +#include "player_exdescs.hpp" const char *CCHAR; @@ -942,13 +943,13 @@ const char *render_ware_for_viewer(struct obj_data *ware, bool privileged, bool return render_buf; } -void look_at_char(struct char_data * i, struct char_data * ch) +void look_at_char(struct char_data * i, struct char_data * ch, const char *used_keyword) { int j, found, weight; float height; struct obj_data *tmp_obj; - +#ifdef USE_JANKY_EQUIPMENT_CONCEAL_SYSTEM if (((GET_EQ(i, WEAR_HEAD) && GET_OBJ_VAL(GET_EQ(i, WEAR_HEAD), 7) > 1) || (GET_EQ(i, WEAR_FACE) && GET_OBJ_VAL(GET_EQ(i, WEAR_FACE), 7) > 1)) && success_test(GET_INT(ch) + GET_POWER(ch, ADEPT_IMPROVED_PERCEPT), @@ -966,6 +967,7 @@ void look_at_char(struct char_data * i, struct char_data * ch) else if (GET_EQ(i, WEAR_UNDER)) send_to_char(ch, GET_EQ(i, WEAR_UNDER)->text.look_desc); } else +#endif { if (i->player.physical_text.look_desc) send_to_char(i->player.physical_text.look_desc, ch); @@ -977,6 +979,17 @@ void look_at_char(struct char_data * i, struct char_data * ch) send_to_char("", ch); } + if (CHAR_HAS_EXDESCS(i) && used_keyword && *used_keyword) { + char uppercase[strlen(used_keyword) + 1]; + for (size_t idx = 0; idx < strlen(used_keyword); idx++) { uppercase[idx] = toupper(used_keyword[idx]); } + uppercase[strlen(used_keyword)] = '\0'; + + send_to_char(ch, "%s %s extra descriptions set. Use ^WLOOK %s EXDESCS^n for more.\r\n", + CAP(HSSH(i)), + HASHAVE(i), + uppercase); + } + if (i != ch && GET_HEIGHT(i) > 0 && GET_WEIGHT(i) > 0) { if ((GET_HEIGHT(i) % 10) < 5) height = (float)(GET_HEIGHT(i) - (GET_HEIGHT(i) % 10)) / 100; @@ -1328,7 +1341,7 @@ void list_one_char(struct char_data * i, struct char_data * ch) } } - if (GET_MOBALERT(i) == MALERT_ALARM && (MOB_FLAGGED(i, MOB_HELPER) || MOB_FLAGGED(i, MOB_GUARD))) { + if (mob_is_alarmed_by_ch(i, ch) && (MOB_FLAGGED(i, MOB_HELPER) || MOB_FLAGGED(i, MOB_GUARD))) { strlcat(buf, "^r(alarmed)^n ", sizeof(buf)); } @@ -1538,7 +1551,7 @@ void list_one_char(struct char_data * i, struct char_data * ch) } } - if (GET_MOBALERT(i) == MALERT_ALARM && (MOB_FLAGGED(i, MOB_HELPER) || MOB_FLAGGED(i, MOB_GUARD))) { + if (mob_is_alarmed_by_ch(i, ch) && (MOB_FLAGGED(i, MOB_HELPER) || MOB_FLAGGED(i, MOB_GUARD))) { strlcat(buf, " ^r(alarmed)^n", sizeof(buf)); } @@ -2715,7 +2728,10 @@ void look_at_target(struct char_data * ch, char *arg) /* Is the target a character? */ if (found_char != NULL) { - look_at_char(found_char, ch); + // If this isn't an exdesc invocation, look at the character. + if (!look_at_exdescs(ch, found_char, arg)) { + look_at_char(found_char, ch, arg); + } /* if (ch != found_char && !ch->char_specials.rigging) { if (CAN_SEE(found_char, ch)) @@ -2729,7 +2745,11 @@ void look_at_target(struct char_data * ch, char *arg) { found_char = get_char_veh(ch, arg, ch->in_veh); if (found_char) { - look_at_char(found_char, ch); + // If this isn't an exdesc invocation, look at the character. + if (!look_at_exdescs(ch, found_char, arg)) { + look_at_char(found_char, ch, arg); + } + /* if (ch != found_char) { if (CAN_SEE(found_char, ch)) { send_to_char(found_char, "%s looks at you.\r\n", GET_NAME(ch)); @@ -2737,6 +2757,7 @@ void look_at_target(struct char_data * ch, char *arg) snprintf(buf, sizeof(buf), "%s looks at %s.\r\n", GET_NAME(ch), GET_NAME(found_char)); send_to_veh(buf, ch->in_veh, ch, found_char, FALSE); } + */ ch->in_room = was_in; return; } @@ -7132,7 +7153,7 @@ const char *render_room_for_scan(struct char_data *ch, struct room_data *room, s snprintf(ENDOF(results), sizeof(results) - strlen(results), " %s%s^n%s\r\n", flag_line, their_name, - FIGHTING(list) == ch ? " ^R(fighting you!)^n" : (GET_MOBALERT(list) == MALERT_ALARM && (MOB_FLAGGED(list, MOB_HELPER) || MOB_FLAGGED(list, MOB_GUARD)) ? " ^r(alarmed)^n" : "")); + FIGHTING(list) == ch ? " ^R(fighting you!)^n" : (mob_is_alarmed_by_ch(list, ch) && (MOB_FLAGGED(list, MOB_HELPER) || MOB_FLAGGED(list, MOB_GUARD)) ? " ^r(alarmed)^n" : "")); } } diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 27d7e6910..8fef97afb 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -32,7 +32,7 @@ /* external functs */ int special(struct char_data * ch, int cmd, char *arg); -void death_cry(struct char_data * ch); +void death_cry(struct char_data * ch, idnum_t cause_of_death_idnum); bool perform_fall(struct char_data *); bool check_fall(struct char_data *, int, bool need_to_send_fall_message); extern int modify_target(struct char_data *); @@ -324,8 +324,7 @@ bool should_tch_see_chs_movement_message(struct char_data *viewer, struct char_d // Spotting someone sneaking around puts NPCs on edge. if (IS_NPC(viewer) && !IS_PROJECT(viewer)) { // Extend their alert time and ensure they're at least Alert. - GET_MOBALERT(viewer) = MAX(GET_MOBALERT(viewer), MALERT_ALERT); - GET_MOBALERTTIME(viewer) = MAX(GET_MOBALERTTIME(viewer), 10); + extend_mob_alarm_time(viewer, actor, 10); // Send messages, but only for the room you're walking into. if (is_arriving) { @@ -552,7 +551,7 @@ int do_simple_move(struct char_data *ch, int dir, int extra, struct char_data *v #ifdef DEATH_FLAGS if (ROOM_FLAGGED(ch->in_room, ROOM_DEATH) && !IS_NPC(ch) && !IS_SENATOR(ch)) { send_to_char("You feel the world slip into darkness, you better hope a wandering DocWagon finds you.\r\n", ch); - death_cry(ch); + death_cry(ch, 0); act("$n vanishes into thin air.", FALSE, ch, 0, 0, TO_ROOM); death_penalty(ch); for (struct obj_data *bio = ch->bioware; bio; bio = bio->next_content) @@ -703,7 +702,7 @@ bool perform_fall(struct char_data *ch) if (ROOM_FLAGGED(ch->in_room, ROOM_DEATH) && !IS_NPC(ch) && !IS_SENATOR(ch)) { send_to_char("You feel the world slip into darkness, you better hope a wandering DocWagon finds you.\r\n", ch); - death_cry(ch); + death_cry(ch, 0); act("$n vanishes into thin air.", FALSE, ch, 0, 0, TO_ROOM); death_penalty(ch); for (struct obj_data *bio = ch->bioware; bio; bio = bio->next_content) diff --git a/src/act.offensive.cpp b/src/act.offensive.cpp index f2d4b2117..3010b8d2c 100644 --- a/src/act.offensive.cpp +++ b/src/act.offensive.cpp @@ -35,7 +35,7 @@ extern int convert_look[]; extern const char *KILLER_FLAG_MESSAGE; /* extern functions */ -void raw_kill(struct char_data * ch); +void raw_kill(struct char_data * ch, idnum_t cause_of_death_idnum); extern void range_combat(struct char_data *ch, char target[MAX_INPUT_LENGTH], struct obj_data *weapon, int range, int dir); extern int find_weapon_range(struct char_data *ch, struct obj_data *weapon); @@ -472,7 +472,7 @@ ACMD(do_kill) mudlog(buf2, vict, LOG_DEATHLOG, TRUE); } - raw_kill(vict); + raw_kill(vict, GET_IDNUM(ch)); } } } diff --git a/src/act.other.cpp b/src/act.other.cpp index e34f5a188..b8bf387fb 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -88,7 +88,7 @@ ACMD(do_quit) send_to_char("You have to type quit - no less, to quit!\r\n", ch); return; } - void die(struct char_data * ch); + void die(struct char_data * ch, idnum_t cause_of_death); struct descriptor_data *d, *next_d; if (IS_NPC(ch) || !ch->desc) @@ -114,7 +114,7 @@ ACMD(do_quit) if (GET_POS(ch) <= POS_STUNNED) { send_to_char("You die before your time...\r\n", ch); act("$n gives up the struggle to live...", TRUE, ch, 0, 0, TO_ROOM); - die(ch); + die(ch, 0); } else { GET_LAST_IN(ch) = GET_ROOM_VNUM(ch->in_room); struct room_data *save_room = ch->in_room; diff --git a/src/act.wizard.cpp b/src/act.wizard.cpp index ce2889017..2a1ca73ab 100644 --- a/src/act.wizard.cpp +++ b/src/act.wizard.cpp @@ -1965,10 +1965,18 @@ void do_stat_mobile(struct char_data * ch, struct char_data * k) MOB_FLAGS(k).PrintBits(buf2, MAX_STRING_LENGTH, action_bits, MOB_MAX); snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "NPC flags: ^c%s^n\r\n", buf2); - snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "Alert status: ^c%s^n with ^c%d^n ticks cooldown.\r\n", - GET_MOBALERT(k) == MALERT_ALARM ? "^rAlarm^n" : (GET_MOBALERT(k) == MALERT_ALERT ? "^yAlert^n" : "^gCalm^n"), - GET_MOBALERTTIME(k) - ); + if (mob_is_alert(k)) { + snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "Alarmed at ^c%ld^n characters: ", GET_MOB_ALARM_MAP(k).size()); + for (auto itr : GET_MOB_ALARM_MAP(k)) { + if (itr.first == 0) { + snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "", itr.second - time(0)); + } else { + const char *ch_name = get_player_name(itr.first); + snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "%s %lds>", ch_name, itr.second - time(0)); + delete [] ch_name; + } + } + } snprintf(ENDOF(buf), sizeof(buf) - strlen(buf), "Height: %d cm, Weight: %d kg\r\n", GET_HEIGHT(k), GET_WEIGHT(k)); diff --git a/src/awake.hpp b/src/awake.hpp index a291f264e..0759c1383 100644 --- a/src/awake.hpp +++ b/src/awake.hpp @@ -2376,11 +2376,6 @@ enum { #define IC_SHIFT 6 #define IC_TRAP 7 -/* MOB Alert Levels */ -#define MALERT_CALM 0 -#define MALERT_ALERT 1 -#define MALERT_ALARM 2 - #define LIGHT 1 #define MODERATE 2 #define SERIOUS 3 diff --git a/src/chargen.cpp b/src/chargen.cpp index 56a6e64ff..15fdbe874 100644 --- a/src/chargen.cpp +++ b/src/chargen.cpp @@ -45,6 +45,14 @@ ACMD_DECLARE(do_help); void echo_on(struct descriptor_data * d); void echo_off(struct descriptor_data * d); +int get_ccr_race_point_cost(int race_int); + +#define PRIORITY_E 4 +#define PRIORITY_D 3 +#define PRIORITY_C 2 +#define PRIORITY_B 1 +#define PRIORITY_A 0 + /********* EXPECTED FLOW: - name @@ -1439,55 +1447,8 @@ void create_parse(struct descriptor_data *d, const char *arg) d->ccr.pr[PO_MAGIC] = CCR_MAGIC_NONE; // Assign racial costs and subtract them from the point value. - switch (GET_RACE(CH)) { - case RACE_HUMAN: - d->ccr.pr[PO_RACE] = 0; - break; - case RACE_DWARF: - case RACE_ORK: - d->ccr.pr[PO_RACE] = 5; - break; - case RACE_OGRE: - case RACE_HOBGOBLIN: - case RACE_GNOME: - case RACE_ONI: - case RACE_SATYR: - case RACE_KOBOROKURU: - case RACE_TROLL: - case RACE_ELF: - case RACE_MENEHUNE: - d->ccr.pr[PO_RACE] = 10; - break; - case RACE_CYCLOPS: - case RACE_FOMORI: - case RACE_GIANT: - case RACE_MINOTAUR: - case RACE_NIGHTONE: - case RACE_WAKYAMBI: - case RACE_GHOUL_DWARF: - case RACE_GHOUL_ORK: - case RACE_DRAKE_HUMAN: - d->ccr.pr[PO_RACE] = 15; - break; - case RACE_DRYAD: - d->ccr.pr[PO_RACE] = 15; - break; - case RACE_GHOUL_ELF: - case RACE_GHOUL_TROLL: - case RACE_DRAKE_DWARF: - case RACE_DRAKE_ORK: - d->ccr.pr[PO_RACE] = 20; - break; - case RACE_DRAKE_ELF: - case RACE_DRAKE_TROLL: - d->ccr.pr[PO_RACE] = 25; - break; - case RACE_WESTERN_DRAGON: - case RACE_EASTERN_DRAGON: - case RACE_FEATHERED_SERPENT: - d->ccr.pr[PO_RACE] = 30; - break; - } + d->ccr.pr[PO_RACE] = get_ccr_race_point_cost(GET_RACE(CH)); + d->ccr.points -= d->ccr.pr[PO_RACE]; d->ccr.pr[5] = -1; @@ -1658,47 +1619,49 @@ void create_parse(struct descriptor_data *d, const char *arg) return; // fall through case CCR_PRESTIGE_RACE_PAID_FOR: - if (GET_RACE(d->character) == RACE_HUMAN) - d->ccr.pr[4] = PR_RACE; - else if (GET_RACE(d->character) == RACE_DWARF) - d->ccr.pr[3] = PR_RACE; - else if (GET_RACE(d->character) == RACE_ORK) - d->ccr.pr[3] = PR_RACE; - else if (GET_RACE(d->character) == RACE_TROLL) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_ELF) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_CYCLOPS) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_KOBOROKURU) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_FOMORI) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_MENEHUNE) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_HOBGOBLIN) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_GIANT) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_GNOME) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_ONI) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_WAKYAMBI) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_OGRE) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_MINOTAUR) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_SATYR) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) == RACE_NIGHTONE) - d->ccr.pr[2] = PR_RACE; - else if (GET_RACE(d->character) >= RACE_DRYAD && GET_RACE(d->character) <= RACE_GHOUL_TROLL) { - if (GET_RACE(d->character) == RACE_GHOUL_HUMAN) - d->ccr.pr[2] = PR_RACE; - else - d->ccr.pr[1] = PR_RACE; + { + switch (GET_RACE(CH)) { + case RACE_HUMAN: + d->ccr.pr[4] = PR_RACE; + break; + case RACE_DWARF: + case RACE_ORK: + d->ccr.pr[3] = PR_RACE; + break; + case RACE_OGRE: + case RACE_HOBGOBLIN: + case RACE_GNOME: + case RACE_ONI: + case RACE_SATYR: + case RACE_KOBOROKURU: + case RACE_TROLL: + case RACE_ELF: + case RACE_MENEHUNE: + d->ccr.pr[2] = PR_RACE; + break; + case RACE_CYCLOPS: + case RACE_FOMORI: + case RACE_GIANT: + case RACE_MINOTAUR: + case RACE_NIGHTONE: + case RACE_WAKYAMBI: + case RACE_GHOUL_DWARF: + case RACE_GHOUL_ORK: + case RACE_DRAKE_HUMAN: + case RACE_DRYAD: + case RACE_GHOUL_ELF: + case RACE_GHOUL_TROLL: + case RACE_DRAKE_DWARF: + case RACE_DRAKE_ORK: + case RACE_DRAKE_ELF: + case RACE_DRAKE_TROLL: + case RACE_WESTERN_DRAGON: + case RACE_EASTERN_DRAGON: + case RACE_FEATHERED_SERPENT: + default: + d->ccr.pr[1] = PR_RACE; + break; + } } if (real_object(OBJ_MAP_OF_SEATTLE) > -1) @@ -2240,4 +2203,66 @@ void refund_chargen_prestige_syspoints_if_needed(struct char_data *ch) { SEND_TO_Q(buf, ch->desc); find_or_load_ch_cleanup(payer); +} + +int get_ccr_race_priority(int race_int) { + switch (get_ccr_race_point_cost(race_int)) { + case 0: + return PRIORITY_E; + case 5: + return PRIORITY_D; + case 10: + return PRIORITY_C; + case 15: + return PRIORITY_B; + default: + return PRIORITY_A; + } +} + +int get_ccr_race_point_cost(int race_int) { + switch (race_int) { + case RACE_HUMAN: + return 0; + case RACE_DWARF: + case RACE_ORK: + return 5; + case RACE_OGRE: + case RACE_HOBGOBLIN: + case RACE_GNOME: + case RACE_ONI: + case RACE_SATYR: + case RACE_KOBOROKURU: + case RACE_TROLL: + case RACE_ELF: + case RACE_MENEHUNE: + case RACE_GHOUL_HUMAN: + return 10; + case RACE_CYCLOPS: + case RACE_FOMORI: + case RACE_GIANT: + case RACE_MINOTAUR: + case RACE_NIGHTONE: + case RACE_WAKYAMBI: + case RACE_GHOUL_DWARF: + case RACE_GHOUL_ORK: + case RACE_DRAKE_HUMAN: + case RACE_DRYAD: + return 15; + case RACE_GHOUL_ELF: + case RACE_GHOUL_TROLL: + case RACE_DRAKE_DWARF: + case RACE_DRAKE_ORK: + return 20; + case RACE_DRAKE_ELF: + case RACE_DRAKE_TROLL: + return 25; + case RACE_WESTERN_DRAGON: + case RACE_EASTERN_DRAGON: + case RACE_FEATHERED_SERPENT: + return 30; + default: + mudlog_vfprintf(NULL, LOG_SYSLOG, "SYSERR: Race %d has no point value in chargen.", race_int); + return 50; + } } \ No newline at end of file diff --git a/src/db.cpp b/src/db.cpp index f150c3073..15c9ee2a2 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -986,8 +986,7 @@ void index_boot(int mode) case DB_BOOT_MOB: // here I am booting with 100 extra slots for creation mob_proto = new struct char_data[rec_count + mob_chunk_size]; - memset((char *) mob_proto, 0, (sizeof(struct char_data) * - (rec_count + mob_chunk_size))); + // memset((char *) mob_proto, 0, (sizeof(struct char_data) * (rec_count + mob_chunk_size))); #ifdef USE_DEBUG_CANARIES for (int i = 0; i < rec_count + mob_chunk_size; i++) diff --git a/src/debug.cpp b/src/debug.cpp index 4ccb55a60..37dc8c537 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -186,6 +186,7 @@ ACMD(do_debug) { } } send_to_char(ch, "OK, set all Renraku-flagged mobs to Renraku faction. Make sure to save.\r\n"); + return; } if (is_abbrev(arg1, "cleardriverflag")) { diff --git a/src/fight.cpp b/src/fight.cpp index 4e625bfda..017e58875 100644 --- a/src/fight.cpp +++ b/src/fight.cpp @@ -373,8 +373,7 @@ void set_fighting(struct char_data * ch, struct char_data * vict, ...) if (IS_NPC(ch)) { // Prevents you from surprising someone who's attacking you already. - GET_MOBALERTTIME(ch) = MAX(GET_MOBALERTTIME(ch), 20); - GET_MOBALERT(ch) = MAX(MALERT_ALERT, GET_MOBALERT(ch)); + set_mob_alarm(ch, vict, 20); } if (!AWAKE(ch)) { @@ -831,7 +830,7 @@ void make_corpse(struct char_data * ch) } } -void death_cry(struct char_data * ch) +void death_cry(struct char_data * ch, idnum_t cause_of_death_idnum) { bool should_make_mechanical_noise_instead_of_scream = IS_NPC(ch) && MOB_FLAGGED(ch, MOB_INANIMATE); @@ -851,10 +850,7 @@ void death_cry(struct char_data * ch) } for (struct char_data *listener = get_ch_in_room(ch)->people; listener; listener = listener->next_in_room) { - if (IS_NPC(listener)) { - GET_MOBALERTTIME(listener) = 30; - GET_MOBALERT(listener) = MALERT_ALARM; - } + set_mob_alarm(listener, cause_of_death_idnum, 30); } struct room_data *was_in = ch->in_room; @@ -868,17 +864,15 @@ void death_cry(struct char_data * ch) act("Somewhere close, you hear someone's death cry!", FALSE, ch, 0, 0, TO_ROOM); } // This specific use of in_room is okay since it's set above and guaranteed to exist. - for (struct char_data *listener = ch->in_room->people; listener; listener = listener->next_in_room) - if (IS_NPC(listener)) { - GET_MOBALERTTIME(listener) = 30; - GET_MOBALERT(listener) = MALERT_ALERT; - } + for (struct char_data *listener = ch->in_room->people; listener; listener = listener->next_in_room) { + set_mob_alarm(listener, cause_of_death_idnum, 30); + } ch->in_room = was_in; } } } -void raw_kill(struct char_data * ch) +void raw_kill(struct char_data * ch, idnum_t cause_of_death_idnum) { struct obj_data *bio, *obj, *o; struct room_data *dest_room; @@ -913,7 +907,7 @@ void raw_kill(struct char_data * ch) { struct room_data *in_room = get_ch_in_room(ch); - death_cry(ch); + death_cry(ch, cause_of_death_idnum); if (!(IS_SPIRIT(ch) || IS_ANY_ELEMENTAL(ch))) make_corpse(ch); @@ -1035,7 +1029,7 @@ void death_penalty(struct char_data *ch) } } -void die(struct char_data * ch) +void die(struct char_data * ch, idnum_t cause_of_death_idnum) { // If they're ready for docwagon retrieval, save them. if (PLR_FLAGGED(ch, PLR_DOCWAGON_READY)) { @@ -1070,7 +1064,7 @@ void die(struct char_data * ch) AFF_FLAGS(ch).RemoveBit(AFF_HEALED); - raw_kill(ch); + raw_kill(ch, cause_of_death_idnum); } ACMD(do_dw_retrieve) @@ -1141,7 +1135,7 @@ ACMD(do_die) mudlog(buf, ch, LOG_DEATHLOG, TRUE); /* Now we just kill them, MuHahAhAhahhaAHhaAHaA!!...or something */ - die(ch); + die(ch, 0); return; } @@ -2402,10 +2396,7 @@ void docwagon_retrieve(struct char_data *ch) { if (FIGHTING(tmp) == ch) { stop_fighting(tmp); } - if (IS_NPC(tmp)) { - GET_MOBALERT(tmp) = MALERT_CALM; - GET_MOBALERTTIME(tmp) = 0; - } + clear_mob_alarm(tmp, ch); } return; @@ -3700,7 +3691,7 @@ bool raw_damage(struct char_data *ch, struct char_data *victim, int dam, int att } } } - die(victim); + die(victim, GET_IDNUM(ch)); return TRUE; } return did_docwagon; @@ -4171,8 +4162,7 @@ void remove_throwing(struct char_data *ch) void combat_message_process_single_ranged_response(struct char_data *ch, struct char_data *tch) { if (IS_NPC(tch)) { // Everyone ends up on edge when they hear gunfire nearby. - GET_MOBALERTTIME(tch) = 20; - GET_MOBALERT(tch) = MALERT_ALERT; + set_mob_alert(tch, 20); // Only guards and helpers who are not in combat can participate. if (CH_IN_COMBAT(tch) || !(MOB_FLAGGED(tch, MOB_GUARD) || MOB_FLAGGED(tch, MOB_HELPER))) @@ -4181,8 +4171,7 @@ void combat_message_process_single_ranged_response(struct char_data *ch, struct // Guards and helpers will actively try to fire on a player using a gun. if (!IS_NPC(ch) && (!FIGHTING(ch) || IS_NPC(FIGHTING(ch)))) { if (number(0, 6) >= 2) { - GET_MOBALERTTIME(tch) = 30; - GET_MOBALERT(tch) = MALERT_ALARM; + set_mob_alarm(tch, ch, 30); struct room_data *was_in = tch->in_room; if (ranged_response(ch, tch) && tch->in_room == was_in) { act("$n aims $s weapon at a distant threat!", @@ -4230,8 +4219,7 @@ void combat_message_process_single_ranged_response(struct char_data *ch, struct return; // Line of sight established, fire. - GET_MOBALERTTIME(tch) = 30; - GET_MOBALERT(tch) = MALERT_ALARM; + set_mob_alarm(tch, FIGHTING(ch), 30); struct room_data *was_in = tch->in_room; if (ranged_response(FIGHTING(ch), tch) && tch->in_room == was_in) { act("$n aims $s weapon at a distant threat!", @@ -4451,11 +4439,9 @@ void combat_message(struct char_data *ch, struct char_data *victim, struct obj_d for (struct char_data *listener = ch_room->people; listener; listener = listener->next_in_room) { if (IS_NPC(listener)) { if (veh || CAN_SEE(listener, ch)) { - GET_MOBALERTTIME(listener) = 30; - GET_MOBALERT(listener) = MALERT_ALARM; + set_mob_alarm(listener, ch, 30); } else { - GET_MOBALERTTIME(listener) = 20; - GET_MOBALERT(listener) = MALERT_ALERT; + set_mob_alert(listener, 20); } } } @@ -5472,8 +5458,7 @@ void range_combat(struct char_data *ch, char *target, struct obj_data *weapon, if (CAN_SEE(vict, ch)) { ranged_response(ch, vict); } else { - GET_MOBALERT(vict) = MALERT_ALARM; - GET_MOBALERTTIME(vict) = MAX(GET_MOBALERTTIME(vict), 30); + set_mob_alert(vict, 30); } } @@ -5851,7 +5836,7 @@ void roll_individual_initiative(struct char_data *ch) // flag until we process all that. if (FIGHTING(ch) && IS_NPC(FIGHTING(ch)) - && GET_MOBALERT(FIGHTING(ch)) == MALERT_CALM + && !mob_is_alert(FIGHTING(ch)) && !MOB_FLAGGED(FIGHTING(ch), MOB_INANIMATE) && !AFF_FLAGGED(FIGHTING(ch), AFF_SURPRISE) && success_test(GET_REA(ch), 4) > success_test(GET_REA(FIGHTING(ch)), 4)) { @@ -6685,12 +6670,15 @@ void chkdmg(struct veh_data * veh) // Dump out and destroy objects. for (struct obj_data *obj = veh->contents, *nextobj; obj; obj = nextobj) { + const char *repr = generate_new_loggable_representation(obj); + nextobj = obj->next_content; switch(number(0, 2)) { - /* case 0: the item stays in the vehicle */ case 0: + // Item stays in vehicle, no-op. break; case 1: + // Item is disgorged to room. obj_from_room(obj); if (veh->in_room) { obj_to_room(obj, veh->in_room); @@ -6700,16 +6688,17 @@ void chkdmg(struct veh_data * veh) mudlog("SYSERR: Destroyed veh had no in_room or in_veh! Disgorging object to Dante's.", NULL, LOG_SYSLOG, TRUE); obj_to_room(obj, &world[real_room(RM_DANTES_GARAGE)]); } + mudlog_vfprintf(NULL, LOG_WRECKLOG, "Disgorged vehicle-held ''%s'' to %s: Wreck dump", repr, obj->in_veh ? GET_VEH_NAME(obj->in_veh) : GET_ROOM_NAME(obj->in_room)); break; case 2: { - char purgebuf[500]; - snprintf(purgebuf, sizeof(purgebuf), "Destroying vehicle-held %s (%ld): Wreck loss", GET_OBJ_NAME(obj), GET_OBJ_VNUM(obj)); - mudlog(purgebuf, NULL, LOG_WRECKLOG, TRUE); + mudlog_vfprintf(NULL, LOG_WRECKLOG, "Destroying vehicle-held ''%s'': Wreck loss", repr); } extract_obj(obj); break; } + + delete [] repr; } } return; @@ -6720,26 +6709,19 @@ bool vram(struct veh_data * veh, struct char_data * vict, struct veh_data * tveh int power, damage_total = 0, veh_dam = 0; int veh_resist = 0, vict_resist = 0, modbod = 0; - // Alarm all NPCs inside the ramming vehicle. + // Alert all NPCs inside the ramming vehicle. for (struct char_data *npc = veh->people; npc; npc = npc->next_in_veh) { - if (IS_NPC(npc)) { - GET_MOBALERT(npc) = MALERT_ALARM; - GET_MOBALERTTIME(npc) = 30; - } + set_mob_alert(npc, 30); } if (vict && IS_NPC(vict)) { - GET_MOBALERT(vict) = MALERT_ALARM; - GET_MOBALERTTIME(vict) = 30; + set_mob_alarm(vict, veh->owner, 30); } // Alarm all NPCs inside the target vehicle. if (tveh) { for (struct char_data *npc = tveh->people; npc; npc = npc->next_in_veh) { - if (IS_NPC(npc)) { - GET_MOBALERT(npc) = MALERT_ALARM; - GET_MOBALERTTIME(npc) = 30; - } + set_mob_alarm(npc, veh->owner, 30); } } diff --git a/src/invis_resistance_tests.cpp b/src/invis_resistance_tests.cpp index 049fbc2b3..f540b34a6 100644 --- a/src/invis_resistance_tests.cpp +++ b/src/invis_resistance_tests.cpp @@ -73,9 +73,9 @@ bool process_spotted_invis(struct char_data *ch, struct char_data *vict) { return FALSE; // Refresh alert time to 20 since we're seeing a PC being shady. - GET_MOBALERTTIME(ch) = MAX(GET_MOBALERTTIME(ch), 20); + extend_mob_alarm_time(ch, vict, 20); - if (GET_MOBALERT(ch) != MALERT_ALARM) { + if (mob_is_alert(ch)) { // The more secure an area is, the twitchier the guards are. This evaluates TRUE if a given guard is twitchy. int alert_threshold = GET_SECURITY_LEVEL(get_ch_in_room(ch)) * 5; if (MOB_FLAGS(ch).AreAnySet(MOB_GUARD, MOB_AGGRESSIVE, MOB_AGGR_ELF, MOB_AGGR_ORK, MOB_AGGR_DWARF, MOB_AGGR_HUMAN, MOB_AGGR_TROLL, ENDBIT)) { @@ -87,14 +87,14 @@ bool process_spotted_invis(struct char_data *ch, struct char_data *vict) { // Seeing someone walking around invis alarms you, or alerts you if you're not twitchy. if (alert_state_should_be_alarm) { - GET_MOBALERT(ch) = MALERT_ALARM; + set_mob_alarm(ch, vict, 10); send_npc_newly_alarmed_message(ch, vict); if (access_level(vict, LVL_PRESIDENT) && PRF_FLAGGED(vict, PRF_ROLLS)) { send_to_char(vict, "You have been noticed while invisible! ^L[%s#%ld has become ^ralarmed^L!]\r\n", GET_CHAR_NAME(ch), GET_MOB_UNIQUE_ID(ch)); } return TRUE; } else { - GET_MOBALERT(ch) = MALERT_ALERT; + set_mob_alarm(ch, NULL, 10); if (access_level(vict, LVL_PRESIDENT) && PRF_FLAGGED(vict, PRF_ROLLS)) { send_to_char(vict, "You have been noticed while invisible! ^L[%s#%ld has become ^yalert^L!]\r\n", GET_CHAR_NAME(ch), GET_MOB_UNIQUE_ID(ch)); } @@ -132,14 +132,18 @@ bool can_see_through_invis(struct char_data *ch, struct char_data *vict) { if ((found = map_to_operate_on->find(idnum)) != map_to_operate_on->end()) { // Anti-cheese: Prevents someone running in and out again, waiting for them to lose memory of us, and then going in while they're calm. if (IS_NPC(ch) && !IS_NPC(vict) && found->second) { - if (GET_MOBALERT(ch) == MALERT_CALM) { + if (!mob_is_alert(ch)) { // They timed out, so let's run the whole spotted check again. if (process_spotted_invis(ch, vict)) { send_npc_newly_alarmed_message(ch, vict); } } else { // Just refresh their existing alert time. - GET_MOBALERTTIME(ch) = MAX(GET_MOBALERTTIME(ch), 20); + if (mob_is_alarmed_by_ch(ch, vict)) { + set_mob_alarm(ch, vict, 20); + } else { + set_mob_alarm(ch, NULL, 20); + } } } @@ -269,9 +273,8 @@ bool can_see_through_invis(struct char_data *ch, struct char_data *vict) { } } else if (GET_EQ(vict, WEAR_LIGHT) && IS_NPC(ch)) { - // You can't see them, but their flashlight beam is still visible. - GET_MOBALERT(ch) = MALERT_ALERT; - GET_MOBALERTTIME(ch) = 20; + // You can't see them, but their flashlight beam is still visible. Set to Alert at minimum. + set_mob_alarm(ch, NULL, 20); } } diff --git a/src/mobact.cpp b/src/mobact.cpp index ef46ecdd7..b9d4732b6 100644 --- a/src/mobact.cpp +++ b/src/mobact.cpp @@ -64,7 +64,7 @@ extern int process_elevator(struct room_data *room, struct char_data *ch, int cm bool memory(struct char_data *ch, struct char_data *vict); int violates_zsp(int security, struct char_data *ch, int pos, struct char_data *mob); bool attempt_reload(struct char_data *mob, int pos); -bool vehicle_is_valid_mob_target(struct veh_data *veh, bool alarmed, idnum_t quest_id); +bool vehicle_is_valid_mob_target(struct veh_data *veh, struct char_data *npc, idnum_t quest_id); void switch_weapons(struct char_data *mob, int pos); void send_mob_aggression_warnings(struct char_data *pc, struct char_data *mob); bool mob_cannot_be_aggressive(struct char_data *ch); @@ -372,17 +372,15 @@ bool vict_is_valid_aggro_target(struct char_data *ch, struct char_data *vict) { return FALSE; bool is_aggressive = MOB_FLAGS(ch).IsSet(MOB_AGGRESSIVE) && !(IS_GHOUL(ch) && IS_GHOUL(vict)); - bool is_alarmed_guard = MOB_FLAGGED(ch, MOB_GUARD) && GET_MOBALERT(ch) == MALERT_ALARM; - bool is_alarmed_racist = mob_is_aggressive(ch, FALSE) && GET_MOBALERT(ch) == MALERT_ALARM && !(IS_GHOUL(ch) && IS_GHOUL(vict)); + bool is_alarmed = _mob_is_alarmed_by_ch(ch, GET_IDNUM(vict)); bool is_racially_motivated = mob_is_aggressive_towards_race(ch, GET_RACE(vict)); bool is_faction_motivated = faction_leader_says_geef_ze_maar_een_pak_slaag_voor_papa(ch, vict) && (MOB_FLAGGED(ch, MOB_HELPER) || MOB_FLAGGED(ch, MOB_GUARD) || is_aggressive); - if (is_aggressive || is_alarmed_guard || is_alarmed_racist || is_racially_motivated || is_faction_motivated) { + if (is_aggressive || is_alarmed || is_racially_motivated || is_faction_motivated) { #ifdef MOBACT_DEBUG - snprintf(buf3, sizeof(buf3), "vict_is_valid_aggro_target: Target found (conditions: %s/%s/%s/%s/%s): %s.", + snprintf(buf3, sizeof(buf3), "vict_is_valid_aggro_target: Target found (conditions: %s/%s/%s/%s): %s.", is_aggressive ? "base aggro" : "!ba", - is_alarmed_guard ? "alarmed guard" : "!ag", - is_alarmed_racist ? "alarmed racist" : "!ar", + is_alarmed ? "alarmed" : "!alm", is_racially_motivated ? "racial violence" : "!rv", is_faction_motivated ? "faction" : "!f", GET_CHAR_NAME(vict)); @@ -399,22 +397,6 @@ bool vict_is_valid_aggro_target(struct char_data *ch, struct char_data *vict) { return TRUE; } - // We allow alarmed, non-aggro NPCs to attack, but only if the victim could otherwise hurt them, and only if they're otherwise aggressive (guard, helper, etc) - if (GET_MOBALERT(ch) == MALERT_ALARM && (MOB_FLAGGED(ch, MOB_HELPER) || MOB_FLAGGED(ch, MOB_GUARD))) { -#ifdef MOBACT_DEBUG - snprintf(buf3, sizeof(buf3), "vict_is_valid_aggro_target: I am alarmed, so %s is a valid aggro target.", GET_CHAR_NAME(vict)); - do_say(ch, buf3, 0, 0); -#endif - if (is_dissuaded_by_hardened_armor(ch, vict)) { -#ifdef MOBACT_DEBUG - strlcpy(buf3, "...But I'm refusing to attack due to hardened armor.", sizeof(buf3)); - do_say(ch, buf3, 0, 0); -#endif - return FALSE; - } - return TRUE; - } - return FALSE; } @@ -552,7 +534,7 @@ void mobact_change_firemode(struct char_data *ch) { proning_desire = -1000; // We're inclined to stand up if we're not in combat or are in combat with someone in our same room. - if ((GET_MOBALERT(ch) == MALERT_CALM && !FIGHTING(ch)) || (FIGHTING(ch) && FIGHTING(ch)->in_room == ch->in_room)) { + if ((!mob_is_alert(ch) && !FIGHTING(ch)) || (FIGHTING(ch) && FIGHTING(ch)->in_room == ch->in_room)) { // Note the -3 here-- someone with FA and a tripod will stay prone. proning_desire -= 3; } @@ -739,7 +721,7 @@ bool mobact_process_in_vehicle_guard(struct char_data *ch) { continue; // Check for our usual conditions. - if (vehicle_is_valid_mob_target(tveh, GET_MOBALERT(ch) == MALERT_ALARM, GET_MOB_QUEST_CHAR_ID(ch))) { + if (vehicle_is_valid_mob_target(tveh, ch, GET_MOB_QUEST_CHAR_ID(ch))) { // Found a target, stop processing vehicles. #ifdef MOBACT_DEBUG snprintf(buf3, sizeof(buf), "m_p_i_v_g: Target found, attacking veh %s.", GET_VEH_NAME(tveh)); @@ -849,30 +831,22 @@ bool mobact_process_in_vehicle_aggro(struct char_data *ch) { return FALSE; } - if (!mob_is_aggressive(ch, TRUE) && GET_MOBALERT(ch) != MALERT_ALARM) { + if (!mob_is_aggressive(ch, TRUE) && mob_is_alert(ch)) { #ifdef MOBACT_DEBUG - strncpy(buf3, "m_p_i_v_a: I am neither aggressive nor alarmed.", sizeof(buf)); + strncpy(buf3, "m_p_i_v_a: I am neither aggressive nor alert.", sizeof(buf)); do_say(ch, buf3, 0, 0); #endif return FALSE; } // Attack vehicles (but not for aggr-to-race) - if (MOB_FLAGGED(ch, MOB_AGGRESSIVE) || (GET_MOBALERT(ch) == MALERT_ALARM && !MOB_FLAGGED(ch, MOB_WIMPY))) { - bool area_has_pc_occupants = FALSE; - for (struct char_data *check_ch = in_room->people; !area_has_pc_occupants && check_ch; check_ch = check_ch->next_in_room) - area_has_pc_occupants = !IS_NPC(check_ch); - + if (MOB_FLAGGED(ch, MOB_AGGRESSIVE) || (mob_is_alert(ch) && !MOB_FLAGGED(ch, MOB_WIMPY))) { for (tveh = in_room->vehicles; tveh; tveh = tveh->next_veh) { // No attacking your own vehicle. if (tveh == ch->in_veh) continue; - // If nobody's in the room or in the vehicle, you can't attack. - if (!area_has_pc_occupants && !tveh->people && !tveh->rigger) - continue; - - if (vehicle_is_valid_mob_target(tveh, TRUE, GET_MOB_QUEST_CHAR_ID(ch))) { + if (vehicle_is_valid_mob_target(tveh, ch, GET_MOB_QUEST_CHAR_ID(ch))) { // Found a valid target, stop looking. #ifdef MOBACT_DEBUG snprintf(buf3, sizeof(buf), "m_p_i_v_a: Target found, attacking veh %s.", GET_VEH_NAME(tveh)); @@ -968,43 +942,33 @@ bool mobact_process_aggro(struct char_data *ch, struct room_data *room) { return FALSE; } - if (!mob_is_aggressive(ch, TRUE) && GET_MOBALERT(ch) != MALERT_ALARM) { + if (!mob_is_aggressive(ch, TRUE) && mob_is_alert(ch)) { #ifdef MOBACT_DEBUG - strncpy(buf3, "m_p_a: I am neither aggressive nor alarmed.", sizeof(buf)); + strncpy(buf3, "m_p_a: I am neither aggressive nor alert.", sizeof(buf)); do_say(ch, buf3, 0, 0); #endif return FALSE; } // Attack vehicles (but not for aggr-to-race) - if (MOB_FLAGGED(ch, MOB_AGGRESSIVE) || (GET_MOBALERT(ch) == MALERT_ALARM && !MOB_FLAGGED(ch, MOB_WIMPY))) { + if (MOB_FLAGGED(ch, MOB_AGGRESSIVE) || (mob_is_alert(ch) && !MOB_FLAGGED(ch, MOB_WIMPY))) { // If I am not astral, am in the same room, and am willing to attack a vehicle this round (coin flip), pick a fight with a vehicle. if (ch->in_room->number == room->number && !IS_ASTRAL(ch) && number(0, 1)) { - bool area_has_pc_occupants = FALSE; - - for (struct char_data *check_ch = room->people; !area_has_pc_occupants && check_ch; check_ch = check_ch->next_in_room) - area_has_pc_occupants = !IS_NPC(check_ch); - for (veh = room->vehicles; veh; veh = veh->next_veh) { - // If nobody's in the room or in the vehicle, you can't attack. - if (!area_has_pc_occupants && !veh->people && !veh->rigger) - continue; - // Aggros don't care about road/garage status, so they act as if always alarmed. - if (vehicle_is_valid_mob_target(veh, TRUE, GET_MOB_QUEST_CHAR_ID(ch))) { + if (vehicle_is_valid_mob_target(veh, ch, GET_MOB_QUEST_CHAR_ID(ch))) { stop_fighting(ch); if (MOB_FLAGGED(ch, MOB_INANIMATE)) { - snprintf(buf, sizeof(buf), "%s$n swivels aggressively towards %s!", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_VEH_NAME(veh)); - snprintf(buf2, sizeof(buf2), "%s%s swivels aggressively towards your vehicle!\r\n", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "" , GET_CHAR_NAME(ch)); + snprintf(buf, sizeof(buf), "$n swivels aggressively towards %s!", GET_VEH_NAME(veh)); + snprintf(buf2, sizeof(buf2), "%s swivels aggressively towards your vehicle!\r\n", CAP(GET_CHAR_NAME(ch))); send_to_char(ch, "You prepare to attack %s!\r\n", GET_VEH_NAME(veh)); } else { - snprintf(buf, sizeof(buf), "%s$n glares at %s, preparing to attack it!", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_VEH_NAME(veh)); - snprintf(buf2, sizeof(buf2), "%s%s glares at your vehicle, preparing to attack!\r\n", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "" , GET_CHAR_NAME(ch)); + snprintf(buf, sizeof(buf), "$n glares at %s, preparing to attack it!", GET_VEH_NAME(veh)); + snprintf(buf2, sizeof(buf2), "%s glares at your vehicle, preparing to attack!\r\n", CAP(GET_CHAR_NAME(ch))); send_to_char(ch, "You prepare to attack %s!\r\n", GET_VEH_NAME(veh)); } - act(buf, TRUE, ch, NULL, NULL, TO_ROOM); send_to_veh(buf2, veh, NULL, TRUE); @@ -1089,9 +1053,9 @@ void send_mob_aggression_warnings(struct char_data *pc, struct char_data *mob) { if (net_succ > 0) { if (pc->in_room == mob->in_room) { if (MOB_FLAGGED(mob, MOB_INANIMATE)) { - snprintf(buf, sizeof(buf), "^y%s$n swivels towards you threateningly!^n", GET_MOBALERT(mob) == MALERT_ALARM ? "Searching for more aggressors, " : ""); + strlcpy(buf, "^y$n swivels towards you threateningly!^n", sizeof(buf)); } else { - snprintf(buf, sizeof(buf), "^y%s$n glares at you, preparing to attack!^n", GET_MOBALERT(mob) == MALERT_ALARM ? "Searching for more aggressors, " : ""); + strlcpy(buf, "^y$n glares at you, preparing to attack!^n", sizeof(buf)); } act(buf, TRUE, mob, NULL, pc, TO_VICT); } else { @@ -1254,7 +1218,7 @@ bool npc_vs_vehicle_blocked_by_quest_protection(idnum_t quest_id, struct veh_dat return TRUE; } -bool vehicle_is_valid_mob_target(struct veh_data *veh, bool alarmed, idnum_t quest_id) { +bool vehicle_is_valid_mob_target(struct veh_data *veh, struct char_data *npc, idnum_t quest_id) { struct room_data *room = veh->in_room; // Vehicle's not in a room? Skip. @@ -1278,23 +1242,27 @@ bool vehicle_is_valid_mob_target(struct veh_data *veh, bool alarmed, idnum_t que if (veh->damage >= VEH_DAM_THRESHOLD_DESTROYED) return FALSE; - // We don't attack vehicles in their proper places-- unless we're alarmed. - if (!alarmed && (ROOM_FLAGGED(room, ROOM_ROAD) || ROOM_FLAGGED(room, ROOM_GARAGE))) - return FALSE; - // Check for quest protections. if (npc_vs_vehicle_blocked_by_quest_protection(quest_id, veh)) return FALSE; // TODO: Should we skip vehicles we can't harm? - // We attack player-owned vehicles, no matter who's in them. - if (veh->owner > 0) + // If we're aggro, we'll fight anything. We'll fight God if we have to. + if (mob_is_aggressive(npc, TRUE)) return TRUE; - // We attack player-occupied vehicles. + // We attack vehicles owned by people we're alarmed at. + if (_mob_is_alarmed_by_ch(npc, veh->owner)) + return TRUE; + + // We attack vehicles rigged by people we're alarmed at. + if (veh->rigger && _mob_is_alarmed_by_ch(npc, GET_IDNUM(veh->rigger))) + return TRUE; + + // We attack vehicles occupied by players we're alarmed at. for (struct char_data *vict = veh->people; vict; vict = vict->next_in_veh) - if (vict->desc && vict->desc->idle_ticks < 10) + if (vict->desc && vict->desc->idle_ticks < 10 && _mob_is_alarmed_by_ch(npc, GET_IDNUM(vict))) return TRUE; // Otherwise, no good. @@ -1341,19 +1309,16 @@ bool mobact_process_guard(struct char_data *ch, struct room_data *room) { continue; // If the room we're in is neither a road nor a garage, attack any vehicles we see. Never attack vehicles in a garage. - if (vehicle_is_valid_mob_target(veh, - GET_MOBALERT(ch) == MALERT_ALARM && !ROOM_FLAGGED(room, ROOM_GARAGE) && !MOB_FLAGGED(ch, MOB_WIMPY), - GET_MOB_QUEST_CHAR_ID(ch))) - { + if (vehicle_is_valid_mob_target(veh, ch, GET_MOB_QUEST_CHAR_ID(ch))) { stop_fighting(ch); if (MOB_FLAGGED(ch, MOB_INANIMATE)) { - snprintf(buf, sizeof(buf), "%s$n swivels threateningly towards %s, preparing to attack it for security infractions!", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_VEH_NAME(veh)); - snprintf(buf2, sizeof(buf2), "%s%s swivels threateningly towards your vehicle, preparing to attack over security infractions!\r\n", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_CHAR_NAME(ch)); + snprintf(buf, sizeof(buf), "$n swivels threateningly towards %s, preparing to attack it for security infractions!", GET_VEH_NAME(veh)); + snprintf(buf2, sizeof(buf2), "%s swivels threateningly towards your vehicle, preparing to attack over security infractions!\r\n", GET_CHAR_NAME(ch)); send_to_char(ch, "You prepare to attack %s for security infractions!\r\n", GET_VEH_NAME(veh)); } else { - snprintf(buf, sizeof(buf), "%s$n glares at %s, preparing to attack it for security infractions!", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_VEH_NAME(veh)); - snprintf(buf2, sizeof(buf2), "%s%s glares at your vehicle, preparing to attack over security infractions!\r\n", GET_MOBALERT(ch) == MALERT_ALARM ? "Searching for more aggressors, " : "", GET_CHAR_NAME(ch)); + snprintf(buf, sizeof(buf), "$n glares at %s, preparing to attack it for security infractions!", GET_VEH_NAME(veh)); + snprintf(buf2, sizeof(buf2), "%s glares at your vehicle, preparing to attack over security infractions!\r\n", GET_CHAR_NAME(ch)); send_to_char(ch, "You prepare to attack %s for security infractions!\r\n", GET_VEH_NAME(veh)); } @@ -1403,7 +1368,7 @@ bool mobact_process_self_buff(struct char_data *ch) { } // Buff self, but only act one out of every 11 ticks (on average), and only if we're not going to put ourselves in a drain death loop. - if (GET_MENTAL(ch) >= 1000 && GET_PHYSICAL(ch) >= 1000 && GET_MOBALERT(ch) != MALERT_ALARM) { + if (GET_MENTAL(ch) >= 1000 && GET_PHYSICAL(ch) >= 1000 && !mob_is_alert(ch)) { bool imp_invis = IS_AFFECTED(ch, AFF_SPELLIMPINVIS) || affected_by_spell(ch, SPELL_IMP_INVIS); bool std_invis = IS_AFFECTED(ch, AFF_SPELLINVIS) || affected_by_spell(ch, SPELL_INVIS); int max_force = MIN(12, GET_MAG(ch) / 100); @@ -1785,10 +1750,8 @@ void mobile_activity(void) if (FIGHTING(ch) || FIGHTING_VEH(ch)) continue; - // Cool down mob alert status. - if (GET_MOBALERT(ch) > MALERT_CALM && --GET_MOBALERTTIME(ch) <= 0) { - GET_MOBALERT(ch) = MALERT_CALM; - + // Cool down mob alert status. This is a hack-- !empty() means anything in map, then !mob_is_alert means nothing in map after expiration of contents. + if (!GET_MOB_ALARM_MAP(ch).empty() && !mob_is_alert(ch)) { // If you just entered calm state and have a weapon, you get your ammo back. // check if they have ammo in proto-- if so, paste in proto ammo, if not, give max * 3 normal ammo // real_mobile is guaranteed to resolve here since we're referencing a vnum from an existing NPC @@ -1796,22 +1759,29 @@ void mobile_activity(void) // Copy over their ammo data. We scan the whole thing instead of just their weapon to prevent someone // giving them a holdout pistol so they never regain their ammo stores. - for (int wp = START_OF_AMMO_USING_WEAPONS; wp <= END_OF_AMMO_USING_WEAPONS; wp++) - for (int am = 0; am < NUM_AMMOTYPES; am++) + for (int wp = START_OF_AMMO_USING_WEAPONS; wp <= END_OF_AMMO_USING_WEAPONS; wp++) { + for (int am = 0; am < NUM_AMMOTYPES; am++) { GET_BULLETPANTS_AMMO_AMOUNT(ch, wp, am) = GET_BULLETPANTS_AMMO_AMOUNT(proto_mob, wp, am); + } + } // Carried weapons. - for (struct obj_data *weapon = ch->carrying; weapon; weapon = weapon->next_content) - if (GET_OBJ_TYPE(weapon) == ITEM_WEAPON && IS_GUN(GET_WEAPON_ATTACK_TYPE(weapon)) && GET_WEAPON_MAX_AMMO(weapon) > 0) + for (struct obj_data *weapon = ch->carrying; weapon; weapon = weapon->next_content) { + if (GET_OBJ_TYPE(weapon) == ITEM_WEAPON && IS_GUN(GET_WEAPON_ATTACK_TYPE(weapon)) && GET_WEAPON_MAX_AMMO(weapon) > 0) { ensure_mob_has_ammo_for_weapon(ch, weapon); + } + } // Wielded weapons. - for (int index = 0; index < NUM_WEARS; index++) + for (int index = 0; index < NUM_WEARS; index++) { if (GET_EQ(ch, index) && GET_OBJ_TYPE(GET_EQ(ch, index)) == ITEM_WEAPON && IS_GUN(GET_WEAPON_ATTACK_TYPE(GET_EQ(ch, index))) && GET_WEAPON_MAX_AMMO(GET_EQ(ch, index)) > 0) + { ensure_mob_has_ammo_for_weapon(ch, GET_EQ(ch, index)); + } + } } // Confirm we have the skills to wield our current weapon, otherwise ditch it. @@ -1875,8 +1845,7 @@ void mobile_activity(void) for (struct char_data *occupant = current_room->people; occupant; occupant = occupant->next_in_room) { if (!IS_NPNPC(occupant)) { if (get_faction_status(occupant, GET_MOB_FACTION_IDNUM(ch)) <= faction_statuses::CAUTIOUS) { - GET_MOBALERT(ch) = MAX(GET_MOBALERT(ch), MALERT_ALERT); - GET_MOBALERTTIME(ch) = MAX(GET_MOBALERTTIME(ch), 15); + extend_mob_alarm_time(ch, occupant, 15); break; } } @@ -2273,3 +2242,96 @@ bool mob_is_aggressive_towards_race(struct char_data *ch, int race) { return FALSE; } + +// Make them mad at a specific idnum. +void set_mob_alarm(struct char_data *npc, idnum_t vict_idnum, int seconds_to_alarm) { + if (!IS_NPNPC(npc)) + return; + + // If already alarmed, set alarm time to the MAX of this and the other. + if (GET_MOB_ALARM_MAP(npc).find(vict_idnum) != GET_MOB_ALARM_MAP(npc).end()) { + GET_MOB_ALARM_MAP(npc)[vict_idnum] = MAX(GET_MOB_ALARM_MAP(npc)[vict_idnum], time(0) + seconds_to_alarm); + } + // Otherwise, just set it to the new value. + else { + GET_MOB_ALARM_MAP(npc)[vict_idnum] = time(0) + seconds_to_alarm; + } +} + +// Make them mad at a specific target. Pass NULL to set alert instead. +void set_mob_alarm(struct char_data *npc, struct char_data *ch, int seconds_to_alarm) { + // We allow for a NULL ch in order to specify that a mob is alert but not alarmed by anyone in particular. + idnum_t vict_idnum = (ch ? GET_IDNUM(ch) : 0); + + set_mob_alarm(npc, vict_idnum, seconds_to_alarm); +} + +// Wrapper for set_mob_alarm with NULL PC. +void set_mob_alert(struct char_data *npc, int seconds_to_alarm) { + set_mob_alarm(npc, NULL, seconds_to_alarm); +} + +// If they're already mad at ch, extend time, otherwise alert them for that time. +void extend_mob_alarm_time(struct char_data *npc, struct char_data *ch, int seconds_to_alarm) { + if (!IS_NPNPC(npc)) + return; + + if (mob_is_alarmed_by_ch(npc, ch)) { + set_mob_alarm(npc, ch, seconds_to_alarm); + } else { + set_mob_alert(npc, seconds_to_alarm); + } +} + +bool mob_is_alarmed_by_ch(struct char_data *npc, struct char_data *ch) { + if (!IS_NPNPC(npc)) + return FALSE; + + return _mob_is_alarmed_by_ch(npc, GET_IDNUM(ch)); +} + +bool mob_is_alarmed_by_ch(struct char_data *npc, idnum_t vict_idnum) { + if (!IS_NPNPC(npc)) + return FALSE; + + return _mob_is_alarmed_by_ch(npc, vict_idnum); +} + +bool _mob_is_alarmed_by_ch(struct char_data *npc, idnum_t vict_idnum) { + if (GET_MOB_ALARM_MAP(npc).find(vict_idnum) == GET_MOB_ALARM_MAP(npc).end()) + return FALSE; + + if (GET_MOB_ALARM_MAP(npc)[vict_idnum] < time(0)) { + GET_MOB_ALARM_MAP(npc).erase(vict_idnum); + return FALSE; + } + + return TRUE; +} + +bool mob_is_alert(struct char_data *npc) { + if (!IS_NPNPC(npc)) + return FALSE; + + return _mob_is_alert(npc); +} + +bool _mob_is_alert(struct char_data *npc) { + // Iterate through all entries in the map, erasing ones that have passed. + for (auto it = GET_MOB_ALARM_MAP(npc).cbegin(); it != GET_MOB_ALARM_MAP(npc).cend() /* not hoisted */; /* no increment */) { + if (it->second < time(0)) { + it = GET_MOB_ALARM_MAP(npc).erase(it); + } else { + ++it; + } + } + + // If there are entries in the map, they're alert. + return !GET_MOB_ALARM_MAP(npc).empty(); +} + +void clear_mob_alarm(struct char_data *npc, struct char_data *ch) { + if (GET_MOB_ALARM_MAP(npc).find(GET_IDNUM(ch)) != GET_MOB_ALARM_MAP(npc).end()) { + GET_MOB_ALARM_MAP(npc).erase(GET_MOB_ALARM_MAP(npc).find(GET_IDNUM(ch))); + } +} \ No newline at end of file diff --git a/src/newfight.cpp b/src/newfight.cpp index 449a7d4c8..e483f5794 100644 --- a/src/newfight.cpp +++ b/src/newfight.cpp @@ -22,7 +22,7 @@ #include "newfight.hpp" #include "metrics.hpp" -extern void die(struct char_data * ch); +extern void die(struct char_data * ch, idnum_t cause_of_death); extern bool astral_fight(struct char_data *ch, struct char_data *vict); extern void dominator_mode_switch(struct char_data *ch, struct obj_data *obj, int mode); extern int calculate_vision_penalty(struct char_data *ch, struct char_data *victim); @@ -135,14 +135,14 @@ bool hit_with_multiweapon_toggle(struct char_data *attacker, struct char_data *v act("A ball of coherent light leaps from your Dominator, tearing into $N. With a scream, $E crumples, bubbles, and explodes in a shower of gore!", FALSE, att->ch, 0, def->ch, TO_CHAR); act("A ball of coherent light leaps from $n's Dominator, tearing into $N. With a scream, $E crumples, bubbles, and explodes in a shower of gore!", FALSE, att->ch, 0, def->ch, TO_NOTVICT); act("A ball of coherent light leaps from $n's Dominator, tearing into you! A horrible rending sensation tears through you as your vision fades.", FALSE, att->ch, 0, def->ch, TO_VICT); - die(def->ch); + die(def->ch, GET_IDNUM(att->ch)); return TRUE; case WEAP_CANNON: // Decomposer? Don't just kill your target-- if they're a player, disconnect them. act("A roaring column of force explodes from your Dominator, erasing $N from existence!", FALSE, att->ch, 0, def->ch, TO_CHAR); act("A roaring column of force explodes from $n's Dominator, erasing $N from existence!", FALSE, att->ch, 0, def->ch, TO_NOTVICT); act("A roaring column of force explodes from $n's Dominator, erasing you from existence!", FALSE, att->ch, 0, def->ch, TO_VICT); - die(def->ch); + die(def->ch, GET_IDNUM(att->ch)); if (def->ch->desc) { STATE(def->ch->desc) = CON_CLOSE; close_socket(def->ch->desc); @@ -435,8 +435,7 @@ bool hit_with_multiweapon_toggle(struct char_data *attacker, struct char_data *v //Handle suprise attack/alertness here -- spirits ranged. if (!target_died && IS_NPC(def->ch)) { - GET_MOBALERT(def->ch) = MALERT_ALARM; - GET_MOBALERTTIME(def->ch) = 30; + set_mob_alarm(def->ch, att->ch, 30); } return target_died; } @@ -560,8 +559,7 @@ bool hit_with_multiweapon_toggle(struct char_data *attacker, struct char_data *v //Handle suprise attack/alertness here -- ranged attack failed. if (!target_died && IS_NPC(def->ch)) { - GET_MOBALERT(def->ch) = MALERT_ALARM; - GET_MOBALERTTIME(def->ch) = 30; + set_mob_alarm(def->ch, att->ch, 30); } return target_died; } @@ -1027,8 +1025,7 @@ bool hit_with_multiweapon_toggle(struct char_data *attacker, struct char_data *v //Handle suprise attack/alertness here -- spirits melee. if (!target_died && IS_NPNPC(def->ch)) { - GET_MOBALERT(def->ch) = MALERT_ALARM; - GET_MOBALERTTIME(def->ch) = 30; + set_mob_alarm(def->ch, att->ch, 30); // Process flame aura here since the spirit will otherwise end combat evaluation here. handle_flame_aura(att, def); @@ -1210,8 +1207,7 @@ bool hit_with_multiweapon_toggle(struct char_data *attacker, struct char_data *v } //Handle suprise attack/alertness here -- defender didn't die. if (IS_NPC(def->ch)) { - GET_MOBALERT(def->ch) = MALERT_ALERT; - GET_MOBALERTTIME(def->ch) = 20; + set_mob_alert(def->ch, 20); } } } diff --git a/src/newmagic.cpp b/src/newmagic.cpp index 0b8673bd8..8483a6ae6 100644 --- a/src/newmagic.cpp +++ b/src/newmagic.cpp @@ -930,11 +930,9 @@ void magic_perception(struct char_data *ch, int force, int spell) // Alert/alarm NPCs when they see casting around them. if (alert_state_should_be_alarm) { - GET_MOBALERT(vict) = MAX(MALERT_ALARM, GET_MOBALERT(vict)); - GET_MOBALERTTIME(vict) = MAX(GET_MOBALERTTIME(vict), 20); + extend_mob_alarm_time(vict, ch, 20); } else { - GET_MOBALERT(vict) = MAX(MALERT_ALERT, GET_MOBALERT(vict)); - GET_MOBALERTTIME(vict) = MAX(GET_MOBALERTTIME(vict), 10); + set_mob_alert(vict, 15); } #ifdef GUARDS_ATTACK_MAGES @@ -943,7 +941,7 @@ void magic_perception(struct char_data *ch, int force, int spell) continue; // Guards and fighters don't like people casting magic around them. - if (GET_MOBALERT(vict) == MALERT_ALARM || MOB_FLAGGED(vict, MOB_GUARD) || mob_is_aggressive(vict, TRUE)) { + if (mob_is_alarmed_by_ch(vict, ch) || MOB_FLAGGED(vict, MOB_GUARD) || mob_is_aggressive(vict, TRUE)) { send_mob_aggression_warnings(ch, vict); set_fighting(vict, ch); } diff --git a/src/olc.cpp b/src/olc.cpp index d8fb1b3d7..3018a5e97 100644 --- a/src/olc.cpp +++ b/src/olc.cpp @@ -1657,8 +1657,6 @@ ACMD(do_mdelete) // Wipe out the top entry of the table (it's not needed), then shrink the table. delete &mob_proto[top_of_mobt]; delete &mob_index[top_of_mobt]; - // memset(&mob_proto[top_of_mobt], 0, sizeof(struct char_data)); - // memset(&mob_index[top_of_mobt], 0, sizeof(struct index_data)); top_of_mobt--; // update the zones by decrementing numbers if >= number deleted diff --git a/src/playerdoc.cpp b/src/playerdoc.cpp index f055a53af..145211e65 100644 --- a/src/playerdoc.cpp +++ b/src/playerdoc.cpp @@ -106,8 +106,7 @@ int alert_player_doctors_of_mort(struct char_data *ch, struct obj_data *docwagon act("$n's DocWagon receiver emits a shrill alarm.", TRUE, plr, 0, 0, TO_ROOM); for (struct char_data *mob = plr->in_room->people; mob; mob = mob->next_in_room) { if (IS_NPC(mob) && !mob->desc) { - GET_MOBALERT(mob) = MAX(GET_MOBALERT(mob), MALERT_ALERT); - GET_MOBALERTTIME(mob) = MAX(GET_MOBALERTTIME(mob), 20); + set_mob_alert(mob, 20); } } } else if (plr->in_veh) { @@ -128,8 +127,7 @@ int alert_player_doctors_of_mort(struct char_data *ch, struct obj_data *docwagon act("$n's DocWagon receiver beeps loudly.", TRUE, plr, 0, 0, TO_ROOM); for (struct char_data *mob = plr->in_room->people; mob; mob = mob->next_in_room) { if (IS_NPC(mob) && !mob->desc) { - GET_MOBALERT(mob) = MAX(GET_MOBALERT(mob), MALERT_ALERT); - GET_MOBALERTTIME(mob) = MAX(GET_MOBALERTTIME(mob), 20); + set_mob_alert(mob, 20); } } } else if (plr->in_veh) { diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 1f81b8313..0825f934e 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -44,7 +44,7 @@ extern struct spell_trainer spelltrainers[]; /* extern functions */ void add_follower(struct char_data * ch, struct char_data * leader); extern void docwagon(struct char_data *ch); -extern void die(struct char_data * ch); +extern void die(struct char_data * ch, idnum_t cause_of_death); extern void affect_total(struct char_data * ch); extern struct obj_data *get_first_credstick(struct char_data *ch, const char *arg); extern struct char_data *give_find_vict(struct char_data * ch, char *arg); @@ -3873,7 +3873,7 @@ SPECIAL(circulation_fan) { ch->in_room->name, ch->in_room->number ); mudlog(buf, ch, LOG_DEATHLOG, TRUE); - die(ch); + die(ch, 0); return true; } else if (!cmd) { diff --git a/src/structs.hpp b/src/structs.hpp index a7e927835..ccd579615 100644 --- a/src/structs.hpp +++ b/src/structs.hpp @@ -37,6 +37,7 @@ class Faction; class ApartmentComplex; class Apartment; class ApartmentRoom; +class PCExDesc; /*********************************************************************** * Structures * @@ -698,6 +699,8 @@ struct player_special_data_saved int best_lifestyle; const char *lifestyle_string; + std::vector exdescs; + player_special_data_saved() : wimp_level(0), freeze_level(0), invis_level(0), incog_level(0), load_room(NOWHERE), last_in(0), last_veh(NOTHING), bad_pws(0), automod_counter(0), totem(0), totemspirit(0), @@ -760,18 +763,17 @@ struct mob_special_data ush_int value_death_items; ush_int value_death_karma; ush_int count_death; - byte alert; - int alerttime; vnum_t spare1, spare2; long lasthit; + std::unordered_map alarm_map = {}; + /* Adding a field to this struct? If it's a pointer, or if it's important, add it to utils.cpp's copy_over_necessary_info() to avoid breaking mdelete etc. */ mob_special_data() : last_direction(NORTH), attack_type(0), default_pos(POS_STANDING), active(0), memory(NULL), wait_state(0), quest_id(0), value_death_nuyen(0), - value_death_items(0), value_death_karma(0), count_death(0), - alert(0), alerttime(0), spare1(0), spare2(0), lasthit(0) + value_death_items(0), value_death_karma(0), count_death(0), spare1(0), spare2(0), lasthit(0) { ZERO_OUT_ARRAY(mob_skills, 10); } diff --git a/src/utils.cpp b/src/utils.cpp index 30feffd1b..9556a4382 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3564,8 +3564,7 @@ void copy_over_necessary_info(struct char_data *original, struct char_data *clon REPLICATE(mob_specials.memory); REPLICATE(mob_specials.wait_state); REPLICATE(mob_specials.quest_id); - REPLICATE(mob_specials.alert); - REPLICATE(mob_specials.alerttime); + GET_MOB_ALARM_MAP(clone).insert(GET_MOB_ALARM_MAP(original).begin(), GET_MOB_ALARM_MAP(original).end()); REPLICATE(mob_specials.spare1); REPLICATE(mob_specials.spare2); REPLICATE(points.mental); diff --git a/src/utils.hpp b/src/utils.hpp index e11c25681..bfbf24ac9 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -772,8 +772,19 @@ int get_armor_penalty_grade(struct char_data *ch); #define GET_ACTIVE(ch) ((ch)->mob_specials.active) #define GET_EXTRA(ch) ((ch)->points.extra) #define GET_EXTRA2(ch) ((ch)->points.extra2) -#define GET_MOBALERT(ch) ((ch)->mob_specials.alert) -#define GET_MOBALERTTIME(ch) ((ch)->mob_specials.alerttime) + +#define GET_MOB_ALARM_MAP(npc) ((npc)->mob_specials.alarm_map) +void set_mob_alarm(struct char_data *npc, idnum_t vict_idnum, int seconds_to_alarm); +void set_mob_alarm(struct char_data *npc, struct char_data *ch, int seconds_to_alarm); +void set_mob_alert(struct char_data *npc, int seconds_to_alarm); +void extend_mob_alarm_time(struct char_data *npc, struct char_data *ch, int seconds_to_alarm); +bool mob_is_alarmed_by_ch(struct char_data *npc, struct char_data *ch); +bool mob_is_alarmed_by_ch(struct char_data *npc, idnum_t vict_idnum); +bool mob_is_alert(struct char_data *npc); +void clear_mob_alarm(struct char_data *npc, struct char_data *ch); +// Underline variants are for when you've already guaranteed they're an NPNPC and don't want to spend cycles checking that. +bool _mob_is_alarmed_by_ch(struct char_data *npc, idnum_t vict_idnum); +bool _mob_is_alert(struct char_data *npc); #define CAN_CARRY_W(ch) ((GET_STR(ch) * 10) + 30) #define CAN_CARRY_N(ch) (8 + GET_QUI(ch) + (GET_REAL_LEVEL(ch)>=LVL_BUILDER?50:0))