Index: common/living.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/common/living.c,v retrieving revision 1.74 diff -C5 -r1.74 living.c *** common/living.c 16 Nov 2005 08:16:00 -0000 1.74 --- common/living.c 3 Jan 2006 16:58:38 -0000 *************** *** 1357,1366 **** --- 1357,1371 ---- */ if (op->move_type == 0) op->move_type = MOVE_WALK; else if (op->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) op->move_type &= ~MOVE_WALK; update_ob_speed(op); + + /* It is quite possible that a player's spell costing might have changed, + * so we will check that now. + */ + if (op->type == PLAYER) esrv_update_spells(op->contr); } /* * Returns true if the given player is a legal class. * The function to add and remove class-bonuses to the stats doesn't *************** *** 1560,1573 **** sprintf(buf,"You are now level %d in the %s skill.",op->level,op->name); if(who) new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf); } player_lvl_adj(who,op); /* To decrease more levels */ } } - - /* * Returns how much experience is needed for a player to become * the given level. level should really never exceed max_level */ --- 1565,1578 ---- sprintf(buf,"You are now level %d in the %s skill.",op->level,op->name); if(who) new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf); } player_lvl_adj(who,op); /* To decrease more levels */ } + /* check if the spell data has changed */ + esrv_update_spells(who->contr); } /* * Returns how much experience is needed for a player to become * the given level. level should really never exceed max_level */ Index: doc/Developers/protocol =================================================================== RCS file: /cvsroot/crossfire/crossfire/doc/Developers/protocol,v retrieving revision 1.14 diff -C5 -r1.14 protocol *** doc/Developers/protocol 31 Aug 2005 21:53:08 -0000 1.14 --- doc/Developers/protocol 3 Jan 2006 16:58:55 -0000 *************** *** 519,528 **** --- 519,601 ---- is sent as plaintext numbers. Tag of 0 means to delete all items on the space teh character is standing on. This command only affects the inventory of the object. To fully delete a container object, a delinv followed by a delitem should be issued. + S->C addspell + + Tells the client to add the spell(s) listed to the list of spells + the client knows about. This will be sent at login, and again whenever + new spells are sent. + + The fields are; + - (4 bytes - int) The ID number for the spell item. This is + going to be unique, for each spell and will be used to refer + to it henceforth. + + (2 bytes, signed int) + The level of the spell. + + (2 bytes, signed int) + The time it will take to cast the spell, in server ticks. + + (2 bytes, signed int) + The mana cost to cast the spell (may be zero) + + (2 bytes, signed int) + The grace cost to cast the spell (may be zero) + + (2 bytes, signed int) + The current damage done by the spell. Note that the meaning of + this number is to a large part spell dependent, what damage it + actually does will depend on how the spell works. + + (1 byte, unsigned int) + The skill that the spell uses to be cast, if zero, no skill is + used in the casting of this spell. + The numbers are the same as for request_info skill_info + + (4 bytes, unsigned integer) + The path that the spell belongs to. + The client should determine the effect of this by comparing + these values to both the spell_paths request_info data and the + stats info concerning attunement/repulsion, etc. + + (1 (non-zero) length byte, followed by that many bytes of ascii text) + This is the name of the spell, and should be sent to the server + in order to cast/invoke it. + + (1 length byte (which may be zero) followed by that + many bytes of ascii text) + This is the name to display to the player regarding the spell. + If length is zero, then the should be used instead. + + (2 length bytes (which may be zero) followed by that many + bytes of ascii text) + The description of the spell, + + S->C updspell + + + This updates some spell (of tag) with new values. The flags are 1 byte + andd etermine which values have been updated, and should be re-read. + Not all fields may be updated by this command, only those that can be + changed. + + If new fields are added in future, they will extend the flags bitmask + and the order will remain the LSB order of the flags - that is, the + value associated with bit 1 set is sent first, then bit 2, etc. + + The format of the values is same as the addspell command above. + + Only one spell can be updated with the updspell command. A spell + command should have been sent by the server before an updspell + command is set. + + S->C delspell + Tells the client to remove its information about the spell Tag is a 4 + byte value, the same as the one sent when the spell was added. + ****************************************************************************** COMMANDS RELATING TO THE PLAYER OBJECT/STATS S->C: player All fields are the same as described in item above. The only *************** *** 551,560 **** --- 624,636 ---- -skill experience: this is only set if exp64 is set. This is sent as the skill level, followed by the skill exp (thus, 9 bytes + the stat type byte) + -spell paths: if spellmon is set in setup, the bitmask of attuned, + repelled and denied paths is sent. These are all 32bits + ****************************************************************************** COMMANDS RELATING TO IMAGE INFORMATION TRANSMISSION S->C: anim ... Informs the client of an animation sequence. The client is responsible *************** *** 1165,1174 **** --- 1241,1254 ---- extendedTextInfos (0/1) Toggle sending from server of extended text informations. What lies in this extended info depended on what extended infos the client asked. See toggleextendedtext command for details. + spellmon (0/1) + If set to 1 the client has indicated that it wishes to be + sent the spell list and updated when it changes. + All data in the setup command is in ascii text form. options and values can not have whitepace - the client and server use whitspace to split the options and values. Currently (2001-05-28), the server only sends a setup in response to *************** *** 1305,1318 **** that skill information. Example: 141:lockpicking 142:hiding 143:smithery ! ------------------------------------------------------------------------------ Example Session: --- 1385,1407 ---- that skill information. Example: 141:lockpicking 142:hiding 143:smithery + spell_paths (no parameters) + This returns a list of all spell paths in the game, along with the + number associated with them. This should be used to parse spell_path + data in the stats command. The number is a bitmask but is sent as a + decimal value. + + All data is sent in text format. Format is: + number:name ! eg + 16:missiles ------------------------------------------------------------------------------ Example Session: Index: include/funcpoint.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/funcpoint.h,v retrieving revision 1.15 diff -C5 -r1.15 funcpoint.h *** include/funcpoint.h 5 Dec 2005 23:34:03 -0000 1.15 --- include/funcpoint.h 3 Jan 2006 16:58:55 -0000 *************** *** 56,65 **** --- 56,66 ---- extern void move_duplicator(object *); extern void trap_adjust(object *, int); extern void esrv_send_item(object *, object *); extern void esrv_del_item(player *, int); extern void esrv_update_item(int, object *, object *); + extern void esrv_update_spells(player *); extern void set_darkness_map(mapstruct *m); extern void dragon_ability_gain(object *, int, int); extern void weather_effect(const char *); extern object * find_skill_by_number(object *, int); Index: include/newclient.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/newclient.h,v retrieving revision 1.11 diff -C5 -r1.11 newclient.h *** include/newclient.h 13 Sep 2003 05:01:33 -0000 1.11 --- include/newclient.h 3 Jan 2006 16:58:55 -0000 *************** *** 97,106 **** --- 97,109 ---- #define CS_STAT_GRACE 23 #define CS_STAT_MAXGRACE 24 #define CS_STAT_FLAGS 25 #define CS_STAT_WEIGHT_LIM 26 #define CS_STAT_EXP64 28 + #define CS_STAT_SPELL_ATTUNE 29 + #define CS_STAT_SPELL_REPEL 30 + #define CS_STAT_SPELL_DENY 31 /* Start & end of resistances, inclusive. */ #define CS_STAT_RESIST_START 100 #define CS_STAT_RESIST_END 117 #define CS_STAT_RES_PHYS 100 *************** *** 223,232 **** --- 226,240 ---- #define UPD_NAME 0x10 #define UPD_ANIM 0x20 #define UPD_ANIMSPEED 0x40 #define UPD_NROF 0x80 + /* The flag values sent by the updspell command */ + #define UPD_SP_MANA 0x01 + #define UPD_SP_GRACE 0x02 + #define UPD_SP_DAMAGE 0x04 + #define SOUND_NORMAL 0 #define SOUND_SPELL 1 /* Contains the base information we use to make up a packet we want to send. */ typedef struct SockList { Index: include/newserver.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/newserver.h,v retrieving revision 1.34 diff -C5 -r1.34 newserver.h *** include/newserver.h 28 Oct 2005 18:19:53 -0000 1.34 --- include/newserver.h 3 Jan 2006 16:58:55 -0000 *************** *** 121,130 **** --- 121,131 ---- uint32 image2:1; /* Client wants image2/face2 commands */ uint32 update_look:1; /* If true, we need to send the look window */ uint32 can_write:1; /* Can we write to this socket? */ uint32 has_readable_type:1; /* If true client accept additional text information used to arrange text in books, scrolls, or scripted dialogs */ + uint32 monitor_spells:1; /* Client wishes to be informed when their spell list changes */ uint32 supported_readables; /* each bit is a readable supported by client */ uint32 cs_version, sc_version; /* versions of the client */ enum MapMode mapmode; /* Type of map commands the client wants. */ uint16 look_position; /* start of drawing of look window */ uint8 mapx, mapy; /* How large a map the client wants */ Index: include/player.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/player.h,v retrieving revision 1.43 diff -C5 -r1.43 player.h *** include/player.h 1 Oct 2005 17:46:04 -0000 1.43 --- include/player.h 3 Jan 2006 16:58:58 -0000 *************** *** 146,165 **** uint32 has_hit:1; /* If set, weapon_sp instead of speed will count */ uint32 name_changed:1; /* If true, the player has set a name. */ uint32 peaceful:1; /* If set, won't attack friendly creatures */ uint32 hidden:1; /* If True, player (DM) is hidden from view */ uint32 explore:1; /* if True, player is in explore mode */ ! uint32 no_shout:1; /* if True, player is *not* able to use shout command */ object *last_skill_ob[NUM_SKILLS]; /* the exp object */ sint64 last_skill_exp[NUM_SKILLS]; /* shadow register. if != exp. obj update client */ float weapon_sp; /* Penalties to speed when fighting w speed >ws/10*/ float last_weapon_sp; /* if diff than weapon_sp, update client */ uint16 last_flags; /* fire/run on flags for last tick */ sint32 last_weight; /* Last weight as sent to client; -1 means do not send weight */ sint32 last_weight_limit; /* Last weight limit transmitted to client */ living orig_stats; /* Permanent real stats of player */ living last_stats; /* Last stats as sent to client */ float last_speed; /* Last speed as sent to client */ sint16 last_resist[NROFATTACKS]; /* last resist values sent to client */ int Swap_First; /* First stat player has selected to swap */ --- 146,168 ---- uint32 has_hit:1; /* If set, weapon_sp instead of speed will count */ uint32 name_changed:1; /* If true, the player has set a name. */ uint32 peaceful:1; /* If set, won't attack friendly creatures */ uint32 hidden:1; /* If True, player (DM) is hidden from view */ uint32 explore:1; /* if True, player is in explore mode */ ! uint32 no_shout:1; /* if True, player is *not* able to use shout command */ object *last_skill_ob[NUM_SKILLS]; /* the exp object */ sint64 last_skill_exp[NUM_SKILLS]; /* shadow register. if != exp. obj update client */ float weapon_sp; /* Penalties to speed when fighting w speed >ws/10*/ float last_weapon_sp; /* if diff than weapon_sp, update client */ uint16 last_flags; /* fire/run on flags for last tick */ sint32 last_weight; /* Last weight as sent to client; -1 means do not send weight */ sint32 last_weight_limit; /* Last weight limit transmitted to client */ + uint32 last_path_attuned; /* Last spell attunment sent to client */ + uint32 last_path_repelled; /* Last spell repelled sent to client */ + uint32 last_path_denied; /* Last spell denied sent to client */ living orig_stats; /* Permanent real stats of player */ living last_stats; /* Last stats as sent to client */ float last_speed; /* Last speed as sent to client */ sint16 last_resist[NROFATTACKS]; /* last resist values sent to client */ int Swap_First; /* First stat player has selected to swap */ Index: include/sockproto.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/sockproto.h,v retrieving revision 1.22 diff -C5 -r1.22 sockproto.h *** include/sockproto.h 7 Dec 2005 18:25:54 -0000 1.22 --- include/sockproto.h 3 Jan 2006 16:58:58 -0000 *************** *** 90,97 **** --- 90,101 ---- void draw_client_map1(object *pl); void draw_client_map(object *pl); void esrv_map_scroll(NewSocket *ns, int dx, int dy); void send_plugin_custom_message(object *pl, char *buf); void send_skill_info(NewSocket *ns, char *params); + void send_spell_paths (NewSocket *ns, char *params); + void esrv_update_spells(player *pl); + void esrv_remove_spell(player *pl, object *spell); + void esrv_add_spells(player *pl, object *spell); /* sounds.c */ void play_sound_player_only(player *pl, short soundnum, sint8 x, sint8 y); void play_sound_map(mapstruct *map, int x, int y, short sound_num); Index: include/sproto.h =================================================================== RCS file: /cvsroot/crossfire/crossfire/include/sproto.h,v retrieving revision 1.158 diff -C5 -r1.158 sproto.h *** include/sproto.h 1 Jan 2006 13:03:02 -0000 1.158 --- include/sproto.h 3 Jan 2006 16:59:06 -0000 *************** *** 788,798 **** void init_spells(void); void dump_spells(void); void spell_effect(object *spob, int x, int y, mapstruct *map, object *originator); int min_casting_level(object *caster, object *spell); int caster_level(object *caster, object *spell); ! int SP_level_spellpoint_cost(object *caster, object *spell, int flags); int SP_level_dam_adjust(object *caster, object *spob); int SP_level_duration_adjust(object *caster, object *spob); int SP_level_range_adjust(object *caster, object *spob); object *check_spell_known(object *op, const char *name); object *lookup_spell_by_name(object *op, const char *spname); --- 788,798 ---- void init_spells(void); void dump_spells(void); void spell_effect(object *spob, int x, int y, mapstruct *map, object *originator); int min_casting_level(object *caster, object *spell); int caster_level(object *caster, object *spell); ! sint16 SP_level_spellpoint_cost(object *caster, object *spell, int flags); int SP_level_dam_adjust(object *caster, object *spob); int SP_level_duration_adjust(object *caster, object *spob); int SP_level_range_adjust(object *caster, object *spob); object *check_spell_known(object *op, const char *name); object *lookup_spell_by_name(object *op, const char *spname); Index: server/apply.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/server/apply.c,v retrieving revision 1.154 diff -C5 -r1.154 apply.c *** server/apply.c 1 Jan 2006 20:36:07 -0000 1.154 --- server/apply.c 3 Jan 2006 16:59:20 -0000 *************** *** 1732,1741 **** --- 1732,1742 ---- } new_draw_info_format (NDI_UNIQUE, 0, op, "Type 'bind cast %s", spell->name); new_draw_info (NDI_UNIQUE, 0, op, "to store the spell in a key."); + esrv_add_spells(op->contr, tmp); } /** * Erases spell from player's inventory. */ *************** *** 1753,1762 **** --- 1754,1764 ---- } new_draw_info_format (NDI_UNIQUE|NDI_NAVY, 0, op, "You lose knowledge of %s.", spell); player_unready_range_ob(op->contr, spob); + esrv_remove_spell(op->contr, spob); remove_ob(spob); free_object(spob); } /** Index: server/login.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/server/login.c,v retrieving revision 1.56 diff -C5 -r1.56 login.c *** server/login.c 11 Dec 2005 19:11:32 -0000 1.56 --- server/login.c 3 Jan 2006 16:59:26 -0000 *************** *** 709,718 **** --- 709,719 ---- /* Do this after checking for death - no reason sucking up bandwidth if * the data isn't needed. */ esrv_new_player(op->contr,op->weight+op->carrying); esrv_send_inventory(op, op); + esrv_add_spells(op->contr, NULL); CLEAR_FLAG(op, FLAG_FRIENDLY); /* can_use_shield is a new flag. However, the can_use.. seems to largely come * from the class, and not race. I don't see any way to get the class information Index: server/spell_util.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/server/spell_util.c,v retrieving revision 1.102 diff -C5 -r1.102 spell_util.c *** server/spell_util.c 5 Dec 2005 23:34:04 -0000 1.102 --- server/spell_util.c 3 Jan 2006 16:59:42 -0000 *************** *** 231,241 **** * spell is the spell object. * Note that it is now possible for a spell to cost both grace and * mana. In that case, we return which ever value is higher. */ ! int SP_level_spellpoint_cost(object *caster, object *spell, int flags) { int sp, grace, level = caster_level(caster, spell); if (settings.spellpoint_level_depend == TRUE) { if (spell->stats.sp && spell->stats.maxsp) { --- 231,241 ---- * spell is the spell object. * Note that it is now possible for a spell to cost both grace and * mana. In that case, we return which ever value is higher. */ ! sint16 SP_level_spellpoint_cost(object *caster, object *spell, int flags) { int sp, grace, level = caster_level(caster, spell); if (settings.spellpoint_level_depend == TRUE) { if (spell->stats.sp && spell->stats.maxsp) { Index: socket/init.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/socket/init.c,v retrieving revision 1.43 diff -C5 -r1.43 init.c *** socket/init.c 7 Dec 2005 18:25:56 -0000 1.43 --- socket/init.c 3 Jan 2006 16:59:47 -0000 *************** *** 107,116 **** --- 107,117 ---- ns->image2 = 0; ns->sound = 0; ns->ext2 = 0; ns->ext_title_flag = 1; ns->exp64 = 0; + ns->monitor_spells = 0; ns->mapmode = Map0Cmd; ns->darkness = 1; ns->status = Ns_Add; ns->comment = NULL; ns->old_mode = 0; Index: socket/loop.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/socket/loop.c,v retrieving revision 1.37 diff -C5 -r1.37 loop.c *** socket/loop.c 11 Dec 2005 19:11:32 -0000 1.37 --- socket/loop.c 3 Jan 2006 16:59:47 -0000 *************** *** 162,171 **** --- 162,172 ---- break; } if (!strcmp(buf, "image_info")) send_image_info(ns, params); else if (!strcmp(buf,"image_sums")) send_image_sums(ns, params); else if (!strcmp(buf,"skill_info")) send_skill_info(ns, params); + else if (!strcmp(buf,"spell_paths")) send_spell_paths(ns, params); else Write_String_To_Socket(ns, bigbuf, len); } /** * Handles old socket format. Index: socket/request.c =================================================================== RCS file: /cvsroot/crossfire/crossfire/socket/request.c,v retrieving revision 1.75 diff -C5 -r1.75 request.c *** socket/request.c 10 Dec 2005 14:27:10 -0000 1.75 --- socket/request.c 3 Jan 2006 16:59:55 -0000 *************** *** 153,163 **** strcat(cmdback, param); } else if (!strcmp(cmd,"exp64")) { ns->exp64 = atoi(param); strcat(cmdback, param); ! } else if (!strcmp(cmd,"darkness")) { ns->darkness = atoi(param); strcat(cmdback, param); } else if (!strcmp(cmd,"map1cmd")) { if (atoi(param)) ns->mapmode = Map1Cmd; /* if beyond this size, need to use map1cmd no matter what */ --- 153,166 ---- strcat(cmdback, param); } else if (!strcmp(cmd,"exp64")) { ns->exp64 = atoi(param); strcat(cmdback, param); ! } else if (!strcmp(cmd, "spellmon")) { ! ns->monitor_spells = atoi(param); ! strcat(cmdback, param); ! } else if (!strcmp(cmd,"darkness")) { ns->darkness = atoi(param); strcat(cmdback, param); } else if (!strcmp(cmd,"map1cmd")) { if (atoi(param)) ns->mapmode = Map1Cmd; /* if beyond this size, need to use map1cmd no matter what */ *************** *** 833,843 **** /* Skip ones we won't send */ if (atnr_cs_stat[i]==-1) continue; AddIfShort(pl->last_resist[i], pl->ob->resist[i], ( char )atnr_cs_stat[i]); } } ! rangetostring(pl->ob, buf); /* we want use the new fire & run system in new client */ AddIfString(pl->socket.stats.range, buf, CS_STAT_RANGE); set_title(pl->ob, buf); AddIfString(pl->socket.stats.title, buf, CS_STAT_TITLE); --- 836,850 ---- /* Skip ones we won't send */ if (atnr_cs_stat[i]==-1) continue; AddIfShort(pl->last_resist[i], pl->ob->resist[i], ( char )atnr_cs_stat[i]); } } ! if (pl->socket.monitor_spells) { ! AddIfInt(pl->last_path_attuned, pl->ob->path_attuned, CS_STAT_SPELL_ATTUNE); ! AddIfInt(pl->last_path_repelled, pl->ob->path_repelled, CS_STAT_SPELL_REPEL); ! AddIfInt(pl->last_path_denied, pl->ob->path_denied, CS_STAT_SPELL_DENY); ! } rangetostring(pl->ob, buf); /* we want use the new fire & run system in new client */ AddIfString(pl->socket.stats.range, buf, CS_STAT_RANGE); set_title(pl->ob, buf); AddIfString(pl->socket.stats.title, buf, CS_STAT_TITLE); *************** *** 1929,1933 **** --- 1936,2100 ---- fatal(0); } Send_With_Handling(ns, &sl); free(sl.buf); } + + /** + * This sends the spell path to name mapping. We ignore + * the params - we always send the same info no matter what. + */ + void send_spell_paths (NewSocket *ns, char *params) { + SockList sl; + int i; + + sl.buf = malloc(MAXSOCKBUF); + strcpy(sl.buf,"replyinfo spell_paths\n"); + for(i=0; i MAXSOCKBUF) { + LOG(llevError,"Buffer overflow in send_spell_paths!\n"); + fatal(0); + } + Send_With_Handling(ns, &sl); + free(sl.buf); + } + + /** + * This looks for any spells the player may have that have changed their stats. + * it then sends an updspell packet for each spell that has changed in this way + */ + void esrv_update_spells(player *pl) { + SockList sl; + int flags=0; + object *spell; + if (!pl->socket.monitor_spells) return; + for (spell=pl->ob->inv; spell!=NULL; spell=spell->below) { + if (spell->type == SPELL) { + /* check if we need to update it*/ + if (spell->last_sp != SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA)) { + spell->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA); + flags |= UPD_SP_MANA; + } + if (spell->last_grace != SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE)) { + spell->last_grace = SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE); + flags |= UPD_SP_GRACE; + } + if (spell->last_eat != spell->stats.dam+SP_level_dam_adjust(pl->ob, spell)) { + spell->last_eat = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell); + flags |= UPD_SP_DAMAGE; + } + if (flags !=0) { + sl.buf = malloc(MAXSOCKBUF); + strcpy(sl.buf,"updspell "); + sl.len=strlen((char*)sl.buf); + SockList_AddChar(&sl, flags); + SockList_AddInt(&sl, spell->count); + if (flags & UPD_SP_MANA) SockList_AddShort(&sl, spell->last_sp); + if (flags & UPD_SP_GRACE) SockList_AddShort(&sl, spell->last_grace); + if (flags & UPD_SP_DAMAGE) SockList_AddShort(&sl, spell->last_eat); + flags = 0; + Send_With_Handling(&pl->socket, &sl); + free(sl.buf); + } + } + } + } + + void esrv_remove_spell(player *pl, object *spell) { + if (!pl->socket.monitor_spells) return; + if (!pl || !spell || spell->env != pl->ob) { + LOG(llevError, "Invalid call to esrv_remove_spell"); + return; + } + SockList sl; + sl.buf = malloc(MAXSOCKBUF); + strcpy(sl.buf,"delspell "); + sl.len=strlen((char*)sl.buf); + SockList_AddInt(&sl, spell->count); + Send_With_Handling(&pl->socket, &sl); + free(sl.buf); + }