Go to the documentation of this file.
56 #define MIN_MON_RADIUS 3
82 else if (
npc->enemy == NULL)
112 ||
npc->enemy == owner))
190 if (nx ==
npc->x && ny ==
npc->y) {
209 LOG(
llevDebug,
"monster_find_nearest_living_creature: map %s (%d,%d) has is_alive set but did not find a monster?\n",
m->path, nx, ny);
235 object *attacker, *
tmp = NULL;
237 attacker =
npc->attacked_by;
238 npc->attacked_by = NULL;
275 if (attacker->
count ==
npc->attacked_by_count) {
297 snprintf(
buf,
sizeof(
buf),
"Halt, %s, you are under arrest!",
npc->enemy->name);
348 &&
op->map->darkness > 0
353 int dark = radius/(
op->map->darkness);
385 if (talked && strlen(talked) > 0) {
402 for (i = 0; i < 15; i++) {
410 #define MAX_EXPLORE 5000
444 uint16_t dx =
FABS(ax - bx), dy =
FABS(ay - by);
445 uint16_t diag =
MIN(dx, dy);
446 return (
MAX(dx, dy) - diag) * 2 + diag * 3;
477 const uint16_t map_height = cur_map->
height;
481 const int size = cur_map->
width * map_height;
493 else if (
source->stats.Int < 8) {
496 else if (
source->stats.Int < 13) {
499 if (
source->stats.Wis < 13) {
523 for (i = 1; i <= 8; ++i)
553 distance =
static_cast<path_data *
>(malloc(size *
sizeof(*distance)));
554 if (distance == NULL) {
561 memset(distance, 255,
sizeof(*distance) * size);
564 current = &distance[map_height * target->
x + target->
y];
582 for (i = 1; i < 8; ++i) {
588 for (i = 0; i < 8; ++i) {
589 uint16_t new_distance;
594 dir =
absdir(default_dir+4+dirs[i]);
610 const path_data *newloc = &distance[map_height * newx + newy];
633 assert(map_height *
x +
y >= 0);
634 assert(map_height *
x +
y < size);
637 explore = &distance[map_height *
x +
y];
641 int16_t move_penalty = 0;
643 if (terrain_value > 0) {
650 || ((
source->move_type&
tmp->move_slow) && (
source->move_type&~
tmp->move_slow&~
tmp->move_block) == 0)) {
651 move_penalty += (int16_t)
tmp->move_slow_penalty;
665 const int base_move_cost = ((dir & 1) == 0 ? 3 : 2);
671 + (move_penalty != 0 && terrain_value != 0 ? base_move_cost + base_move_cost * move_penalty * terrain_value / 2 : base_move_cost);
674 if (explore->
distance <= new_distance)
726 if (
op->stats.Con > 0 &&
op->stats.hp <
op->stats.maxhp) {
736 op->last_heal += (
int)((
float)(8*
op->stats.Con)/
FABS(
op->speed));
737 op->stats.hp =
MIN((int32_t)
op->stats.hp+
op->last_heal/32,
op->stats.maxhp);
742 &&
op->stats.hp >= (int16_t)((int32_t)
op->run_away *
op->stats.maxhp / 100))
753 if (
op->stats.Pow > 0 &&
op->stats.sp <
op->stats.maxsp) {
763 op->last_sp += (
int)((
float)(8*
op->stats.Pow)/
FABS(
op->speed));
764 op->stats.sp =
MIN(
op->stats.sp+
op->last_sp/128,
op->stats.maxsp);
797 if (
op->attack_movement&
HI4) {
798 switch (
op->attack_movement&
HI4) {
858 object *owner, *enemy, *part;
927 if (
op->race != NULL && strcmp(
op->race,
"doppleganger") == 0) {
943 op->facing =
op->direction;
1010 LOG(
llevMonster,
"monster %s (%d, %d on %s) can't reach enemy %s (%d, %d on %s)\n",
1011 op->name,
op->x,
op->y,
op->map ?
op->map->name :
"(unknown map)", enemy->
name, enemy->
x, enemy->
y, enemy->
map ? enemy->
map->
path :
"(unknown map)");
1018 switch (
op->attack_movement&
LO4) {
1064 for (diff = 1; diff <= maxdiff; diff++) {
1097 if (nearest_player && nearest_player != enemy && !
monster_can_hit(part, enemy, &rv)) {
1099 enemy = nearest_player;
1156 for (more =
ob2->more; more != NULL; more = more->
more) {
1188 assert(spell_ob != NULL);
1212 #define MAX_KNOWN_SPELLS 20
1248 return altern[
RANDOM()%i];
1284 if (owner != NULL) {
1301 if (spell_item == NULL) {
1307 if (!spell_item->
inv) {
1308 LOG(
llevError,
"spellbook %s does not contain a spell?\n", spell_item->
name);
1311 spell_item = spell_item->
inv;
1337 return cast_spell(part, part, dir, spell_item, NULL);
1371 if (owner != NULL) {
1448 if (owner != NULL) {
1473 LOG(
llevDebug,
"Error: Monster %s (%d) has FLAG_READY_SKILL without skill.\n", head->
name, head->
count);
1497 int at_least_one = 0;
1505 if (owner != NULL) {
1516 if (wand->type ==
WAND) {
1519 if (wand->stats.food <= 0)
1529 if (wand->type ==
ROD && wand->inv) {
1532 if (wand->stats.hp <
MAX(wand->inv->stats.sp, wand->inv->stats.grace))
1550 LOG(
llevError,
"Error: Monster %s (%d) HAS_READY_RANG() without wand/horn/rod.\n", head->
name, head->
count);
1596 while (
x !=
pl->x ||
y !=
pl->y ||
map !=
pl->map) {
1601 LOG(
llevError,
"monster_use_bow: no map but still path exists??\n");
1606 if (owner && owner->
x ==
x && owner->
y ==
y && owner->
map ==
map)
1617 return fire_bow(head, NULL, dir, 0, part->
x, part->
y);
1632 val =
item->stats.dam;
1633 val +=
item->magic*3;
1659 if (other_weap == NULL)
1683 val =
item->stats.ac;
1685 val +=
item->magic*3;
1702 object *other_armour;
1706 if (other_armour == NULL)
1718 if (
item->resist[i] > other_armour->
resist[i])
1720 else if (
item->resist[i] < other_armour->
resist[i])
1794 }
else if (
item->type ==
BOW) {
1856 for (part =
monster; part != NULL; part = part->
more)
1867 if (
tmp->weight > 0) {
1868 int32_t weight_limit;
1876 nrof =
MAX(1,
tmp->nrof);
1917 else switch (
item->type) {
1960 if (
monster->body_info[i] &&
item->body_info[i]) {
1967 if (((!(
monster->pick_up&32)) && flag) || ((
monster->pick_up&32) && (!flag)))
1980 switch (
tmp->type) {
2016 int help_radius = 3;
2020 int override_help_radius;
2022 override_help_radius = strtol(
value, NULL, 10);
2023 if (override_help_radius >= 0 && override_help_radius < 30)
2024 help_radius = override_help_radius;
2026 LOG(
llevDebug,
"monster_npc_call_help: invalid help_radius %d\n", override_help_radius);
2029 for (
int x = -help_radius;
x <= help_radius;
x++)
2030 for (
int y = -help_radius;
y <= help_radius;
y++) {
2032 int16_t sx =
op->x+
x;
2033 int16_t sy =
op->y+
y;
2079 }
else if (
ob->move_status > 20)
2080 ob->move_status = 0;
2091 if (
ob->move_status++ < 25)
2093 else if (
ob->move_status < 50)
2095 ob->move_status = 0;
2111 if (
ob->move_status || inrange)
2114 if (
ob->move_status == 0)
2116 else if (
ob->move_status < 10)
2118 else if (
ob->move_status < 15)
2120 ob->move_status = 0;
2141 if (
ob->stats.maxhp && (
ob->stats.hp*100)/
ob->stats.maxhp <
ob->run_away)
2163 static const int circle [12] = { 3, 3, 4, 5, 5, 6, 7, 7, 8, 1, 1, 2 };
2165 if (++
ob->move_status > 11)
2166 ob->move_status = 0;
2176 static const int circle[20] = { 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 1, 1, 1, 2, 2 };
2178 if (++
ob->move_status > 19)
2179 ob->move_status = 0;
2189 if (
ob->move_status++ > 6)
2190 ob->move_status = 0;
2191 if (
ob->move_status < 4)
2202 if (
ob->move_status++ > 6)
2203 ob->move_status = 0;
2204 if (
ob->move_status < 4)
2215 if (
ob->move_status++ > 16)
2216 ob->move_status = 0;
2217 if (
ob->move_status < 6)
2219 else if (
ob->move_status < 8)
2221 else if (
ob->move_status < 13)
2230 if (
ob->move_status++ > 16)
2231 ob->move_status = 0;
2232 if (
ob->move_status < 6)
2234 else if (
ob->move_status < 8)
2236 else if (
ob->move_status < 13)
2247 if (
ob->move_status < 1
2248 ||
ob->move_status > 8
2250 for (i = 0; i < 5; i++) {
2406 if (orig_map !=
op->map) {
2407 LOG(
llevDebug,
"Warning: Forced to swap out very recent map\n");
2419 snprintf(own,
sizeof(own),
"You say: %s", txt);
2478 value[0] =
'3' + rand() % 6;
2587 if (!
op || !enemy || !
op->map || !enemy->
map)
2615 hide_discovery =
op->stats.Int/5;
2622 int bonus = (
op->level/2)+(
op->stats.Int/5);
2626 if (sk_hide != NULL)
2629 LOG(
llevError,
"monster_can_detect_enemy() got hidden player w/o hiding skill!\n");
2637 hide_discovery +=
bonus*5;
2643 radius = radius/2, hide_discovery = hide_discovery/3;
2651 radius +=
op->map->darkness/2;
2653 radius -=
op->map->darkness/2;
2658 if (radius < MIN_MON_RADIUS && op->
map->darkness < 5 && rv->
distance <= 1)
2680 if (enemy->
hide && rv->
distance <= 1 &&
RANDOM()%100 <= (
unsigned int)hide_discovery) {
2685 "You are discovered by %s!",
2696 if (
RANDOM()%50 <= (
unsigned int)hide_discovery) {
2702 "You see %s noticing your position.",
2729 if (
op->glow_radius > 0)
2767 object *looker =
HEAD(
op);
2789 "Your light reveals your hiding spot!");
2792 }
else if (enemy->
hide)
bool object_value_set(const object *op, const char *const key)
#define GET_MAP_OB(M, X, Y)
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
object * object_get_owner(object *op)
int path_to_player(object *mon, object *pl, unsigned mindiff)
static void monster_pace2_movev(object *ob)
static int monster_check_wakeup(object *op, object *enemy, rv_vector *rv)
static uint16_t estimate_distance(int16_t ax, int16_t ay, int16_t bx, int16_t by)
object * object_find_by_type_subtype(const object *who, int type, int subtype)
sstring replies[MAX_REPLIES]
void remove_friendly_object(object *op)
object * object_find_by_type_applied(const object *who, int type)
#define NUM_BODY_LOCATIONS
int get_randomized_dir(int dir)
object * find_skill_by_number(object *who, int skillno)
void LOG(LogLevel logLevel, const char *format,...)
sstring replies_words[MAX_REPLIES]
#define CAN_APPLY_NOT_MASK
void monster_check_apply_all(object *monster)
#define QUERY_FLAG(xyz, p)
static int monster_disthit_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv)
int pets_should_arena_attack(object *pet, object *owner, object *target)
void make_visible(object *op)
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
#define WILL_APPLY_HANDLE
int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg)
StringBuffer * stringbuffer_new(void)
#define FOR_BELOW_PREPARE(op_, it_)
void object_set_enemy(object *op, object *enemy)
static int monster_check_good_weapon(object *who, object *item)
object * monster_check_enemy(object *npc, rv_vector *rv)
static int monster_run_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv)
void monster_check_doors(object *op, mapstruct *m, int x, int y)
#define FLAG_SEE_INVISIBLE
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
static void monster_check_pickup(object *monster)
static int monster_check_good_armour(object *who, object *item)
void fix_object(object *op)
#define FLAG_READY_SCROLL
static int monster_can_hit(object *ob1, object *ob2, rv_vector *rv)
object * get_nearest_criminal(object *mon)
the faster the spell may be cast there are several other common only the caster may be affected by the spell The most common spell range is that of touch This denotes that the caster much touch the recipient of the spell in order to release the spell monster
Almost all the spell_ *base png files are taken from JXClient s source
int apply_manual(object *op, object *tmp, int aflag)
#define FOR_OB_AND_ABOVE_FINISH()
const char * object_get_value(const object *op, const char *const key)
static object * monster_find_enemy(object *npc, rv_vector *rv)
object * object_insert_in_ob(object *op, object *where)
object * get_nearest_player(object *mon)
int16_t resist[NROFATTACKS]
Plugin animator file specs[Config] name
static void monster_rand_move(object *ob)
#define FOR_BELOW_FINISH()
object * monster_find_nearest_enemy(object *npc, object *owner)
#define FOR_OB_AND_ABOVE_PREPARE(op_)
#define SP_SUMMON_MONSTER
static int monster_wait_att2(int dir, rv_vector *rv)
object * object_find_by_type_and_race(const object *who, int type, const char *race)
static event_registration m
char * stringbuffer_finish(StringBuffer *sb)
void object_free_drop_inventory(object *ob)
object * pets_get_enemy(object *pet, rv_vector *rv)
sstring add_refcount(sstring str)
short freearr_y[SIZEOFFREE]
static int monster_wait_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv)
static int monster_get_armour_quality(const object *item)
void query_name(const object *op, char *buf, size_t size)
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
static int monster_get_weapon_quality(const object *item)
sstring stringbuffer_finish_shared(StringBuffer *sb)
int monster_can_see_enemy(object *op, object *enemy)
sstring add_string(const char *str)
static void monster_apply_below(object *monster)
void void ext_info_map(int color, const mapstruct *map, uint8_t type, uint8_t subtype, const char *str1)
int path_measure_func(const void *ob)
static void monster_circ2_move(object *ob)
int has_carried_lights(const object *op)
static void monster_pace2_moveh(object *ob)
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text followed by possible other data The remaining data can be binary it is up to the client and server to decode what it sent The commands as described below is just the data portion of the packet If writing a new remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in others
TIPS on SURVIVING Crossfire is populated with a wealth of different monsters These monsters can have varying immunities and attack types In some of them can be quite a bit smarter than others It will be important for new players to learn the abilities of different monsters and learn just how much it will take to kill them This section discusses how monsters can interact with players Most monsters in the game are out to mindlessly kill and destroy the players These monsters will help boost a player s after he kills them When fighting a large amount of monsters in a single attempt to find a narrower hallway so that you are not being attacked from all sides Charging into a room full of Beholders for instance would not be open the door and fight them one at a time For there are several maps designed for them Find these areas and clear them out All throughout these a player can find signs and books which they can read by stepping onto them and hitting A to apply the book sign These messages will help the player to learn the system One more always keep an eye on your food If your food drops to your character will soon so BE CAREFUL ! NPCs Non Player Character are special monsters which have intelligence Players may be able to interact with these monsters to help solve puzzles and find items of interest To speak with a monster you suspect to be a simply move to an adjacent square to them and push the double ie Enter your message
static void monster_pace_moveh(object *ob)
#define GET_MAP_MOVE_BLOCK(M, X, Y)
static int is_enemy(object *who, object *owner)
int apply_can_apply_object(const object *who, const object *op)
#define FLAG_UNAGGRESSIVE
static object * monster_choose_random_spell(object *monster)
#define GET_MAP_LIGHT(M, X, Y)
mapstruct * get_map_from_coord(mapstruct *m, int16_t *x, int16_t *y)
#define FLAG_CAN_USE_SKILL
static int monster_can_pick(object *monster, object *item)
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
static StringBuffer * monster_format_say(const object *npc, const char *message)
int monster_compute_path(object *source, object *target, int default_dir)
const Animations * animation
object * monster_find_throw_ob(object *op)
int ob_blocked(const object *ob, mapstruct *m, int16_t x, int16_t y)
static int monster_hitrun_att(int dir, object *ob)
void monster_npc_call_help(object *op)
static int monster_use_range(object *head, object *part, object *pl, int dir)
void * minheap_remove(MinHeap *heap)
uint32_t get_weight_limit(int stat)
void free_string(sstring str)
void pets_move(object *ob)
int move_object(object *op, int dir)
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
#define OUT_OF_REAL_MAP(M, X, Y)
void get_search_arr(int *search_arr)
void ext_info_map_except(int color, const mapstruct *map, const object *op, uint8_t type, uint8_t subtype, const char *str1)
static void monster_circ1_move(object *ob)
void fatal(enum fatal_error err)
#define MSG_TYPE_SKILL_FAILURE
static int monster_move_no_enemy(object *op)
int get_dialog_message(object *op, const char *text, struct_dialog_message **message, struct_dialog_reply **reply)
int object_can_pick(const object *who, const object *item)
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
void monster_do_say(const mapstruct *map, const char *message)
const typedef char * sstring
void animate_object(object *op, int dir)
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
static int monster_move_randomly(object *op)
int monster_move(object *op)
static int monster_use_skill(object *head, object *part, object *pl, int dir)
static int monster_use_bow(object *head, object *part, object *pl, int dir)
void pets_follow_owner(object *ob, object *owner)
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
void do_hidden_move(object *op)
int on_same_map(const object *op1, const object *op2)
#define CLEAR_FLAG(xyz, p)
#define MSG_TYPE_DIALOG_MAGIC_EAR
void minheap_init_static(MinHeap *heap, void **arr, int amt, int(*measure_func)(const void *))
#define MSG_TYPE_DIALOG_NPC
int fire_bow(object *op, object *arrow, int dir, int wc_mod, int16_t sx, int16_t sy)
int get_rangevector(object *op1, const object *op2, rv_vector *retval, int flags)
static void monster_pace_movev(object *ob)
sstring npc_msgs[MAX_NPC]
int monster_stand_in_light(object *op)
void object_remove(object *op)
void monster_do_living(object *op)
int player_can_view(object *pl, object *op)
void monster_npc_say(object *npc, const char *cp)
static int monster_talk_to_npc(object *npc, talk_info *info)
static int monster_use_scroll(object *head, object *part, object *pl, int dir)
int object_set_value(object *op, const char *key, const char *value, int add_key)
#define MSG_TYPE_COMMUNICATION
int8_t get_attr_value(const living *stats, int attr)
void drain_rod_charge(object *rod)
#define WILL_APPLY_TREASURE
int minheap_insert(MinHeap *heap, void *ob)
short freearr_x[SIZEOFFREE]
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire cold Fireborns are supposed to be fire spirits They re closely in tune with magic and are powerful and learn magic easily Being fire they are immune to fire and and vulnerable to cold They are vulnerable to ghosthit and drain because being mostly non anything which strikes directly at the spirit hits them harder race attacktype restrictions immunities prot vuln Quetzalcoatl physical no armour fire cold Quetzalcoatl s are now born knowing the spell of burning but because of their negative wisdom bonus
static int monster_cast_spell(object *head, object *part, object *pl, int dir)
int events_execute_object_say(object *npc, talk_info *talk)
int dirdiff(int dir1, int dir2)
int makes_invisible_to(object *pl, object *mon)
void monster_check_earthwalls(object *op, mapstruct *m, int x, int y)
const char * get_reply_text_own(reply_type rt)
static int monster_dist_att(int dir, object *enemy, object *part, rv_vector *rv)
#define FOR_INV_PREPARE(op_, it_)
#define MSG_TYPE_COMMUNICATION_SAY
void drain_wand_charge(object *wand)
static int monster_should_cast_spell(object *spell_ob)
void monster_communicate(object *op, const char *txt)
static int monster_do_talk_npc(object *npc, talk_info *info)
static void monster_check_apply(object *mon, object *item)
int is_true_undead(object *op)
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
object * identify(object *op)
static const char * get_reply_text_other(reply_type rt)