Index: ChangeLog =================================================================== RCS file: /home/circledb/.cvs/circle/ChangeLog,v retrieving revision 1.211 retrieving revision 1.225 diff -u -p -r1.211 -r1.225 --- ChangeLog 16 Apr 2002 02:23:37 -0000 1.211 +++ ChangeLog 4 Oct 2002 07:18:16 -0000 1.225 @@ -1,5 +1,7 @@ Release history: +Version 3.00 beta pl22 release: October 4, 2002 +Version 3.00 beta pl21 release: April 15, 2002 Version 3.00 beta pl20 release: January 15, 2002 Version 3.00 beta pl19 release: August 14, 2001 Version 3.00 beta pl18 release: March 18, 2001 @@ -4013,3 +4015,173 @@ change. snprintf() and vsnprintf() for platforms lacking them. cnf/configure.in: HAVE_(V)SNPRINTF checks. configure: Regenerated. + +******** Patchlevel 22 *************************************************** + +4/30/2002 + +-- gg - Makefile.lcc, structs.h, constants.c: bpl21 -> bpl22 + +-- gg - class.c: level_exp(): Fixed thief experience progression to be + less jumpy. Old: 10k 20k 30k 70k. + +-- gg - boards.c: find_board(): Check immortals' inventories for boards + so they can carry them around. Requires the config.c + setting 'load_into_inventory = YES' to actually be useful. + +5/1/2002 + +-- gg - shop.c: assign_the_shopkeepers(): Avoid assigning 'shop_keeper' + special procedure to SHOP_FUNC(). + From: "Kras Kresh" + +-- gg - act.movement.c: ok_pick(): Actually use dex_app_skill[].p_locks. + From: Edward J Glamkowski + +-- gg - objsave.c: gen_receptionist(): Make random actions work again. + From: "Kras Kresh" + +-- gg - house.c: House_list_guests(): Print out when all guests listed + are actually deleted instead of leaving empty "Guests:". + From: "Bob Castillo" + +5/2/2002 + +-- gg - handler.c: affect_total(): Bring affect_total() in line with + do_set()'s handling of godly statistics. + From: Edward J Glamkowski + +5/3/2002 + +-- ae - act.informative.c: do_weather(): added info on the actual numbers + for god+. + From: Edward J Glamkowski + +5/17/2002 + +-- gg - utils.c, utils.h: get_filename(): 'name' should be 'const char *'. + +-- gg - interpreter.c: nanny(): Remove aliases upon self-delete. + alias.c: delete_aliases(): New. + +-- gg - conf.h.win: Updated to new 'configure' checks. + sysdep.h: Aliases for (v)snprintf and MAX_PATH for Windows. + +5/19/2002 + +-- ae - building.tex: corrected the order of UNDERWATER and FLYING sector + types. From: nano.vladimir@slsp.sk + +-- ae - shop.c: list_detailed_shop(): A few \r\n markers were missing in the + output, specifically before the Shopkeeper:, Buys:, and + Buy at: lines. + From: Edward J Glamkowski + +6/13/2002 + +-- gg - doc/coding.doc: Beginnings of Chapter 2. + +6/17/2002 + +-- gg - doc/coding.doc: Sections 2.2.1 (partial) and 2.2.3. Some additions + to section 2.4.5. Also ran it through ispell. + +6/21/2002 + +-- gg - doc/coding.doc: Finish (?) section 2.4. Add TODO list. + +-- gg - doc/coding.doc: Finish section 2.2.2. + +-- gg - handler.h: Remove prototypes for non-existant functions: + get_obj, get_obj_in_list, Crash_get_filename. + +-- gg - comm.h: Remove prototype for non-existant function: perform_to_all. + +6/24/2002 + +-- gg - doc/coding.doc: Finish section 2.2.1. + +6/25/2002 + +-- gg - doc/coding.doc: Finish sections 3.6 and 3.7. + +6/28/2002 + +-- gg - act.informative.c: look_at_room(): Use '%s' format for rooms. + +-- gg - act.informative.c: list_one_char(): Missed a '+1' when converting + UPPER() usage. Found by Ken Ray . + +9/06/2002 + +-- ae - building.tex: Fixed a double "action bitvector" description (the + second was a typo for "affection bitvector"). + From Ken Ray + +-- ae - 30.obj: Changed the sdesc of #3003 (firebreather) to 'bottle'. + From Ken Ray + +-- ae - Various READMEs: Changed 'circle30bpl12' to be generic, and fixed some + small typos. From Julian Buckley + +9/25/2002 + +-- gg - act.other.c: do_display(); structs.h: Auto-prompt display. + +-- gg - act.wizard.c: do_wiznet(): Add missing "%s" formats in send_to_char(). + +-- gg - boards.c: Board_remove_msg(): Clear orphaned board message when + shifting the list down. Caused free() problems on shutdown. + +-- gg - comm.c: make_prompt(): Reorganization to make adding bits easier. + +-- gg - act.comm.c: do_gen_comm(): Missing \r\n at end of message. + From: "Thomas Arp" + +-- gg - act.item.c: perform_put(): Don't allow cursed items to be put into + a container not in the character's inventory. + +-- gg - comm.c: affect_update() is in magic.c, not spells.c + From: Mathew Earle Reuther + +-- gg - act.other.c: do_gen_tog(): Add 'bitvector_t' comment on variable. + From: Mike Stilson + +-- gg - act.other.c: do_quit(): Fix load room setting to virtual. + From: "The Fungi" + +-- gg - shop.c: list_detailed_shop(): Fix '>= 0' check to be '!= NOBODY'. + From: "The Fungi" + +-- gg - act.informative.c: do_where(); act.wizard.c: do_show(); + spec_procs.c: list_skills(); utils.c: sprintbit(): + 'nlen' should be 'int', to check return value of snprintf(). + From: Thomas Arp + +-- gg - shop.c: customer_string(): Loop reorganized to be cleaner and + remove a pointless use of strlcpy(). + +10/03/2002 + +-- gg - interpreter.c: perform_dupe_check(): Make immortals that have switched + into disconnected mortal bodies do a return instead of being + disconnected. Noted by: "Kras Kresh" + +-- gg - magic.c: mag_unaffects(): Make SPELL_HEAL special-case into a generic + solution instead. + +-- gg - interpreter.c: nanny(): Destroy d->character if player answers 'N' to + "Did I get that right?" prompt. There may be lingering + information from a deleted character. + From: "Kras Kresh" + +-- gg - shop.c: shopping_list(): Only display "none of those found" message + if actually asked to search for an object. + From: "Ken Ray" + +-- gg - comm.c: game_loop(): Change handling of process_output() and + ->has_prompt. + vwrite_to_output(): Truncate text to fit on overflow instead + of throwing it away. + write_to_descriptor(): Return number of bytes written. + process_output(): Keep data on socket write block. Index: doc/README.AMIGA =================================================================== RCS file: /home/circledb/.cvs/circle/doc/README.AMIGA,v retrieving revision 1.2 retrieving revision 1.3 diff -u -p -r1.2 -r1.3 --- doc/README.AMIGA 4 Aug 1999 23:33:32 -0000 1.2 +++ doc/README.AMIGA 6 Sep 2002 12:17:52 -0000 1.3 @@ -17,7 +17,7 @@ UNIX. What you need is: This can be found at ftp://ftp.ninemoons.com/pub/geekgadgets. Installing this is a bit tricky at first, I recommend reading the manual - first. It's located at http://www.ninemoons.com/GG/docs/GG_7.html. That + first. It is located at http://www.ninemoons.com/GG/docs/GG_7.html. That way you'll know what archives to download and install. * 6 MB's of RAM, it might work with less but has not been tested. @@ -34,7 +34,7 @@ UNIX. What you need is: Here is how you compile this baby: 1) Open up a shell. -2) CD to the circle30bpl15 directory. +2) CD to the circle30bplXX directory. (where 'XX' is the current patchlevel) 3) Type "sh configure". 4) CD to the src directory. 5) Edit the Makefile file and add -m68020 to MYFLAGS. @@ -78,4 +78,4 @@ If someone manages to compile it on a Po I don't own a PowerUP/G3/G4-board myself so I've not been able to test this. If you have any questions or can't get it working, feel free to email me at -boing@amigascne.org. \ No newline at end of file +boing@amigascne.org. Index: doc/README.MSVC4 =================================================================== RCS file: /home/circledb/.cvs/circle/doc/README.MSVC4,v retrieving revision 1.1 retrieving revision 1.2 diff -u -p -r1.1 -r1.2 --- doc/README.MSVC4 1 Jul 1998 19:06:19 -0000 1.1 +++ doc/README.MSVC4 6 Sep 2002 12:17:52 -0000 1.2 @@ -25,7 +25,7 @@ package mentioned in the README.WIN file http://www.circlemud.org/ The latest version will be called something ending in .zip, like - "circle30bpl13.zip". + "circle30bplXX.zip". (where 'XX' is the patchlevel) 2) When you unzip the .zip archive, MAKE SURE to use an unzip program that can handle long filenames. Old versions of pkunzip (e.g. 2.x) do NOT @@ -38,8 +38,9 @@ package mentioned in the README.WIN file the following commands are performed at the MS-DOS prompt. 4) Use the CD command to switch to the main CircleMUD directory. For - exmaple, type "CD \circle30bpl12", but this may vary depending on the - version of Circle you downloaded and where you decided to uncompress it. + example, type "CD \circle30bplXX", where 'XX' is the patchlevel of the + version of Circle you downloaded. Also note that the full path will + depend on where you decided to uncompress it. 5) Go to the src directory and rename conf.h.win to conf.h, and replace the Makefile with Makefile.msvc. This can be accomplished with the Index: doc/README.UNIX =================================================================== RCS file: /home/circledb/.cvs/circle/doc/README.UNIX,v retrieving revision 1.2 retrieving revision 1.3 diff -u -p -r1.2 -r1.3 --- doc/README.UNIX 24 May 1999 22:55:29 -0000 1.2 +++ doc/README.UNIX 6 Sep 2002 12:17:52 -0000 1.3 @@ -27,7 +27,7 @@ can try to make Circle work with every U http://www.circlemud.org/ The latest version will be called something ending in .tar.gz, like - "circle30bpl13.tar.gz". + "circle30bplXX.tar.gz". (where 'XX' is the patchlevel) 2) Unpack the archive. If you have the .tar.gz version, uncompress it Index: doc/coding.doc =================================================================== RCS file: /home/circledb/.cvs/circle/doc/coding.doc,v retrieving revision 1.3 retrieving revision 1.16 diff -u -p -r1.3 -r1.16 --- doc/coding.doc 17 May 2001 14:50:51 -0000 1.3 +++ doc/coding.doc 4 Oct 2002 03:34:42 -0000 1.16 @@ -9,6 +9,7 @@ HOW TO CONVERT YOUR IDEAS INTO REALITY A CircleMUD Coding Manual by Jeremy Elson + and the rest of the CircleMUD Group Summary: A guide to writing C code for use with CircleMUD. Includes a @@ -65,7 +66,7 @@ bad bugs and even syntax errors which pr all. If a potential MUD implementor wanted to run a Diku, an excellent knowledge of C was necessary, because the MUD simply wouldn't run otherwise. - In 1995 the situation is much different. With the proliferation of + Now the situation is much different. With the proliferation of user-friendly code bases such as Merc and Circle, any Average Joe can just type "make" and inflict yet another MUD on the world. Therefore, the number of truly unique MUDs as a fraction of the total is dropping drastically @@ -295,60 +296,1607 @@ practical code examples. 2.2. The Way Things Work -- Overview + +2.2.1. Boot Phase + + CircleMUD is a complex system of code, data files, and external +input all interacting in fun, unexpected ways. As with any program, it +doesn't just spring into existence ready to play, but must cull information +from the administrator and the system itself to determine how it should +begin. + + The first action by CircleMUD on startup is to check for the +existence of any command-line parameters, as seen in the main() function. +These can have radical impact on CircleMUD's operation so it must check +them before any other action. For example, CircleMUD might be given the +'-d' parameter, specifying an alternate library directory, so it cannot have +done any processing on data files prior to the command-line reading. + + After finishing the immediate input, the next step is to be able to +communicate to the outside world. The communication may be either the +"standard error" file descriptor or a file, depending on the command-line +options and whichever CircleMUD succeeds in opening. + + From here there are two possible branches depending on +administrator input. If "Syntax Check" mode is enabled, then we load only +the world. Otherwise, we start initializing the game in preparation for +loading the world and accepting players. Since syntax checking is a subset +of the normal startup phase, this document shall follow only the most +common action of a non-syntax-check boot. + + A few minor items precede the loading of the world: initializing +the random number generator, creating the '.killscript' file, finding (or +guessing) the maximum number of players the operating system will allow +simultaneously, and opening the file descriptor later to be used to accept +connections. This early opening of the "mother" file descriptor is why +there is a period of time during startup where a player connection will be +accepted but not receive a login prompt. CircleMUD does not check for +connection attempts while it loads the world database but the operating +system will complete the connection anyway and post notification to be seen +later. + + The world loading starts by reading in the MUD date and a few +simple text files for player reference. If the date file cannot be loaded, +then a default date is provided for the calculations. After randomly +generating the weather, in the date function, the following user text files +are loaded: news, credits, mortal message of the day, immortal message of +the day, help default message screen, general MUD info, wizard list, +immortal list, policies, immortal handbook, background story, and login +greeting screen. These files are reproduced verbatim by various user +commands and exist for the players and/or administrators to read. The MUD +doesn't interpret these in any way. + + Next, the spell definitions are loaded for player use. The +spello() function gives such important information as casting cost, valid +targets (area, self, etc.), spell type, valid casting positions (sitting, +standing, etc.), spell name, and wear off message. Any skill or spell that +isn't set up via mag_assign_spells() will not be usable even if appropriate +code exists elsewhere that would make it have an effect. Any spell defined +here that does not have appropriate code elsewhere to handle it will do +nothing when used. + + As far as the game world, the zones must be loaded first to define +the structure of the rest of the objects. The 'lib/world/zon/index' file +is first consulted for the list of zone files that are requested to load. +The 'index.mini' file is used in the event mini-MUD mode was requested +earlier on the command line. Each zone file is read in order to pull in +the name, description, reset time, room range, and zone commands. Any +error on loading will result in the MUD aborting startup. The rooms are +read next from 'lib/world/wld/index' in the same manner as the zones, with +the added restriction that each room must fall within the ranges defined by +the previously-loaded zones. This restriction ensures the code can access +the zone record of a room without worrying about any rooms not having an +associated zone. + + The rooms, as loaded, contain virtual numbers of the rooms they are +supposed to exit to. It's slow to do virtual->real number translation +while the MUD is running so all room exits are replaced by their real +number equivalents during startup. Then the pre-defined starting locations +for mortals, immortals, and frozen characters are checked to make sure they +exist. If the mortal start room does not exist, the MUD aborts with an +error. A missing immortal or frozen start room is redirected to the mortal +start room to allow booting to continue. The mortal start room must exist +in a room file loaded by 'lib/world/wld/index.mini' file for mini-MUD mode +to work. + + Mobiles from 'lib/world/mob/index' and objects from +'lib/world/obj/index' are loaded afterward. Mobile and object virtual +numbers need not correspond to the zone number ranges as rooms do but it is +encouraged. There are various sanity checks done to mobiles and objects +that may be printed during startup. Any warnings issued should be fixed +but should not adversely affect the MUD itself. + + In the same manner as the room number virtual->real translation, +the zone reset information is also translated. The zone reset structure +contains a variety of different records so it takes special care to find +the appropriate numbers to translate from virtual->real. Any virtual +numbers that cannot be resolved result in that zone command being disabled. +Such entries have their type set to '*' to avoid the error in the future. + + If special procedures are enabled, as they usually are, the shops +are loaded from 'lib/world/shp/index' last. Contrary to the rooms and +zones, the shops contain lists of virtual numbers to sell so any errors +will only show up when attempting to use the shop. The charisma of a +shopkeeper is important, as well as the buy/sell rates, as they define what +prices the players will pay when shopping. + + The entire help database for CircleMUD is loaded into memory after +the world has been loaded. This should be ~100kB with the stock CircleMUD +help files, but the extra memory used saves the MUD from having to +manipulate the help files to find the appropriate entries after the boot. +Even though the help index is stored by keyword, any entry having multiple +keywords is only stored once for each set. + + An index of the player file is built to allow random access to each +player as attempt to connect or save. The index stores the player's name, +to search by for login, and their ID number, for the mail system to search +by. The array index is their position in the player file, used for loading +and saving. + + Fight messages and socials loaded next are placed by line in their +appropriate categories. The messages and socials themselves aren't +interpreted beyond their placement but they'll be used extensively in the +game. Spells defined earlier from mag_assign_spells() get their battle +messages from this file. Defaults are provided for the battle messages if +none is defined. All socials loaded require a matching entry in the global +command table or they will not be accessible by the players. + + Special procedures must be associated with their now-loaded object +so they're processed now. A virtual number that cannot be resolved for the +special procedure elicits a warning message on startup but a missing +special procedure function will cause a compiler error. Shopkeepers are +assigned via assign_the_shopkeepers() instead of the standard +assign_mobiles() so they can be automatically handled as a result of the +shop files loaded earlier. + + Since the spells are skills were defined earlier, they can then be +assigned to each class upon a certain level. The spells and skills given +to the various classes only depend upon a SPELL_ or SKILL_ definition so +the assignment doesn't need to care if it is a generically handled spell or +a custom implemented skill. The spells and skills may be assigned per the +whims of the administrator based on their view of the appropriate classes. + + The command and spell tables need sorted for special circumstances. +In the case of the command table, it is for the 'commands' input which +displays all known commands for that player's level. The spell table is +sorted for the 'practice' command for easier visual searching by the +player. Neither are critical to the MUD's operation but exist for the +players' benefit. + + The CircleMUD mail system keeps a binary file of all MUD message +sent. It requires an index be built on startup to keep track of which +blocks are free and which blocks have mail messages yet to be delivered. +It is also important for the code to check the mail file to make sure it +hasn't been corrupted somehow. A report on the number of messages present +in the system is printed when finished. + + The stupid-people prevention code of site banning and invalid name +rejection comes next. The site ban code loads a text file list of all +sites that have been deemed unworthy to connect to the MUD. Invalid name +rejection loads a list of substrings that must not appear in any +character's name that is being created. The invalid name list is limited +in the number of entries it may hold but any amount of sites may be banned. +The length of each banned site name is limited, however. + + After deleting any expired rent files, the house code loads up any +abodes defined. It must make sure the rooms still exist and the owner is +still in the game before setting up the house, atrium, and guest list. +Houses aren't loaded in mini-MUD mode since most of the rooms will likely +not exist. + + The final step of the world startup resets every zone. This +populates the world with mobiles, the mobiles with objects, and places +other objects that should be on the ground. From here, all zone timers are +started and will continue to reset (or not) depending on their settings and +the number of people in the zone. The time is recorded after the zone +reset to provide a display of the amount of time the MUD has been running. + + Once the world has finished being loaded, CircleMUD tells the +operating system what sort of signals it wants to receive and which to +ignore. It doesn't want to receive a SIGPIPE, which would abort the +program whenever it tried to write to a player who abruptly disconnected. +The user-defined signals (Unix) SIGUSR1 and SIGUSR2 are set to re-read the +wizard list file and unrestrict the game, respectively. SIGUSR1 is sent by +'autowiz' whenever it finishes writing the file. SIGUSR2 may be sent by +the administrators, using 'kill -USR2 pid-of-mud`, if accidentally banning +themselves from their own MUD. A 3-minute timer signal prevents the MUD +from being stuck in an infinite loop forever. The common shutdown signals +of SIGHUP (Unix), SIGTERM, and SIGINT are mapped to a function that prints +their reception and then quits the program. The children signal, SIGCHLD, +is set up to remove finished 'autowiz' instances. + + From here, the '.killscript' file is removed since if we've made it +this far, we can start successfully. The only thing left now is to enter +the interactive phase in game_loop(), unless the "Syntax Check" option is +enabled. + + +2.2.2. Interactive Phase + + Everything that ever happens while the MUD is interactively +processing players occurs as a descendant of game_loop(). It is responsible +for all network I/O, periodic tasks, and executing player actions. +Consequently, game_loop() has the most concentration of newbie-eating code +in the code base. Care should be taken to fully understand the network I/O +infrastructure before trying to modify it. + + Each second of the game is divided into pulses, or points in time +that network I/O is processed and commands run. This works to limit the +number of possible commands the player can enter per second, such as +speed-walking. If the game falls behind schedule, it will continuously +process the pulses until it has caught up with where it is supposed to be. +If over 30 seconds have passed, only 30 seconds are processed as it would +be computationally expensive to do them all. If there isn't anyone +connected to the game at all, then the MUD sleeps until someone connects. +("If a tree falls in the forest, and no one's around to hear it...") + + The first task of the pulse is to check for network socket input. +First, any pending connections are given descriptors to track them. Then +any descriptors with a socket in the exception set is kicked from the game. +Incoming socket data is read next, checked for command history or repeat +operations, and placed on the appropriate descriptor's command queue. + + Having read commands, they are then set up to be executed. A +player must first be in a condition to execute those commands, so anyone +with a wait state is skipped and people idled are pulled back into the +game. Depending on the player's activities, the input may be sent through +either the message writing system, the text pager, the nanny() login +sequence, the alias system, or straight to the in-game command interpreter. + + In the message writing system (see modify.c), any input, except for +an '@' at the beginning of a line, is simply appended to the string the +character has decided to edit. An '@' at the beginning of a line finishes +the editing and returns the player to their previous state. Typical uses +of this are editing the character's description in the menu or writing to +boards and notes in the game. + + The text pager allows the player to scroll around pages of text the +MUD has produced. It allows refreshing the current page, going back a +page, or continuing down the page. The text pager returns the player to +their previous state upon reaching the end of the text or the user telling +it to quit. + + The nanny() login sequence guides the player through the initial +authentication and entering of the game. Here they are prompted for the +character name and password. Upon successful login they may take actions +such as changing their character's description, changing their password, +deleting the character, or entering the game. + + The alias system provides a method for player's to shortcut the +typing of commands. Each line of input is compared against their existing +aliases and, if a match is found, the desired alias result is applied to +their input and placed on the command queue. This process applies before +the command interpreter so the game need not care what aliases each player +may define. + + Finally, the command interpreter pulls off a line of input from the +player's command queue. It uses the command table in interpreter.c to find +the appropriate function, if any, to call for the given command. It does +various checks to ensure the character is the proper level, in the correct +position, and not frozen. Any applicable special procedures are checked +before running the function in the command table. The special procedure +may override the function completely, as the shopkeepers do with the 'buy' +and 'list' commands, or allow it to execute. + + Processing the commands likely generated output for the player so +the network output is processed next. As much data is sent from the MUD's +output queue as will fit in the operating system's socket buffer for each +player. Any output that can't fit in the socket buffer is held until the +next pulse when perhaps some of the pending output will have been +delivered. If any players decided to exit the game or otherwise +disconnected, their descriptor is marked for removal and the connection +closed. + + Lastly, the periodic tasks are executed via the heartbeat() +function. Each task may run every minute, every 5 minutes, every pulse, or +any other time increment. The list of tasks to run includes: + + * Process each zone's timed updates. + * Disconnect idle descriptors in the login sequence. + * Do basic mobile intelligence and special procedures. + * Determine effects of violence. + * Check the weather. + * Test for magical affect expiration. + * Regenerate health, mana, and movement. + * Auto-save characters. + * Saving the MUD time. + * Extracting dead characters. + + If the user-defined signals SIGUSR1 or SIGUSR2 have arrived, they +are processed at the bottom of the game loop. Doing such work in the +signal handler itself is unsafe and could cause unpredictable behavior. + + +2.2.3. Shutting Down + + The first responsibility on shutdown is to save the players' +characters to disk. CircleMUD tracks which characters have been modified +with a PLR_CRASH flag so it only needs to save those characters which have +changed in the period from the last auto-save to the shutdown. + + To disconnect the network connections, each player socket is closed +in turn. Closing their connection also frees up any memory associated but, +unless memory allocation tracing is enabled, it's not necessary since we're +about to exit anyway. The "mother" descriptor is closed last, preventing +any new players from connecting. + + Left to do are: closing the player database file, saving the +current MUD time for next startup, and logging normal termination of game. +The player database is kept open for fast access to loading and saving +characters as they come and go. Saving the MUD time tries to maintain some +appearance of continuity in your calendar. + + The final actions before exiting are only significant if the +program is running with a memory allocation tracer. Here it sets about +explicitly freeing every known piece of memory previously in use by the +MUD. This is done to leave only forgotten allocations that may indicate a +long-term memory leak in the program. The operating system will remove +even forgotten memory when the program exits but the information may help +prevent an ever-increasing memory usage while running. Memory tracing will +vary depending on your operating system and may not necessarily be +available on your particular platform. + + That's it, show's over. "return 0;" + + 2.3. CircleMUD's Global Variables + + CircleMUD doesn't use objects in the sense of object-oriented +languages so there are various global variables kept that must be manipulated +in order to change the game world. Some are stored as arrays; others as +lists. The global variables kept as arrays generally have an associated +'top_of_...' function to denote its boundary. + + This is not an exhaustive list but the most frequently encountered of +the ones in use. A large number of global, constant strings are kept in +constants.c but they're simply read from. Also see config.c for a number of +configuration global variables. + +2.3.1. World Variables + + * struct room_data *world; + + This array of 'struct room_data' stores all the information for the + rooms of the MUD universe. In addition to room titles, flags, and + descriptions, it also contains lists of people, mobiles, and objects + currently in the rooms. + + * struct char_data *mob_proto; + + While rooms are singular entities, there may be many copies of a + single mobile running around the game world. The 'mob_proto' array of + 'struct char_data' contains the base information for all mobiles, as + well as being used for string sharing amongst all the copies. + + * mob_rnum top_of_mobt; + + The highest valid array index of 'mob_proto' and 'mob_index' is stored + here. This is _not_ the count of items in the array. That's + "top_of_mobt + 1". + + * struct index_data *mob_index; + * struct index_data *obj_index; + + The number of instances of each particular mobile or object is + tracked through these dynamic arrays of 'struct index_data'. This + is also the location for reverse-mapping the mobile/object real + number back into a virtual number. In addition, the special + procedure and several other variables are stored here to avoid + keeping redundant data on every mobile/object in the game. + + * struct obj_data *obj_proto; + + This is analogous to 'mob_proto', except for objects. It's kept as a + dynamic array of 'struct obj_data'. + + * obj_rnum top_of_objt; + + The highest valid array index of 'obj_proto' and 'obj_index' is stored + here. This is _not_ the count of items in the array. That's + "top_of_objt + 1". + + * struct zone_data *zone_table; + + A dynamic array of type 'struct zone_data' storing all the + information for zone resets, titles, and room ranges. + + * zone_rnum top_of_zone_table; + + The highest valid array index of 'zone_table' is stored here. There + are "top_of_zone_table + 1" zones in the array. + +2.3.2. Object Instance Lists + + * struct descriptor_data *descriptor_list; + + All players connected to the MUD have a descriptor used to send the + MUD's output to and receive player input from. Each descriptor does + not necessarily have a character (not logged in yet). These are + stored as a linked list of 'struct descriptor_data' using the 'next' + field. + + * struct char_data *character_list; + + All player characters and mobiles are kept in a linked-list of 'struct + char_data'. This list uses the 'next' field of the structure. + + * struct obj_data *object_list; + + As all characters and descriptors are kept in a linked-list, so too + are all the objects in the world kept. This list uses the 'next' field + of 'struct obj_data'. + +2.3.3. Other + + * const struct command_info cmd_info[]; + + All user commands are kept in a large array in interpreter.c. The + ACMD functions use this array, along with the command array index + variable they are given, to figure out what specific text the user + typed to get to this command function. This allows them to handle + multiple commands with the same function with CMD_IS(). The size of + the array is static and determined by the computer at the time of + compilation. + + * struct weather_data weather_info; + + Raining? Snowing? Weather changes occurring in weather.c are stored in + this structure. The sun's current state (dawn, dusk, etc.) is kept + here as well. + + * struct time_info_data time_info; + + The current date and time of the game world. Used in the shop code + for opening and closing the stores on schedule. + + 2.4. Frequently Used Functions +2.4.1. Basic String Handling + + * int str_cmp (const char *a, const char *b); + * int strn_cmp (const char *a, const char *b, int len); + + These are portable, case-insensitive versions of the strcmp() and + strncmp() functions. Like their C library counterparts, these + functions perform a character-by-character comparison and return the + difference of the first mismatching characters or zero, if the two + strings are equal. The strn_cmp() function only compares the first + 'len' characters of the first string to the second string. + + Many platforms have built-in routines to do case-insensitive string + comparisons. Where applicable, str_cmp() and strn_cmp() are aliases + for the platform-specific equivalents. On platforms without these + functions built-in, CircleMUD will supply a working implementation. + One should prefer the CircleMUD names to ensure portability since it + incurs no run-time performance cost. + + * int isname (const char *str, const char *namelist); + + Compare a string, 'str', to each of a list of space-delimited + keywords, 'namelist', using str_cmp(). This is used for matching + an argument to an object or mobile, which has its keywords arranged + like, "bottle brown beer." + + * bool is_abbrev (const char *str, const char *arg2); + + A case-insensitive substring match. Equivalent to: + "strn_cmp(needle, haystack, strlen(needle)) == 0" Generally this is + user-extended to act like isname(), except for abbreviated keywords. + + * void skip_spaces (char **string); + + The command interpreter hands off " cabbage" if the user types in + "look cabbage". Since comparisons need to be done without the extra + space in the string, this function removes it. It's also used + internally for the argument splitting functions to properly handle + user input such as: "put the cabbage in the bag". + + * bool is_number (const char *str); + + Tests if an entire string is an ASCII-encoded, unsigned decimal + number by performing isdigit() on each character of the string. + Only unsigned (zero or positive) numbers are recognized. + + * char *delete_doubledollar (char *string); + + The MUD, in processing input, converts a single dollar sign to a + double dollar sign. If you want to echo out a user's input through + something other than act(), you will want to smash '$$' into '$' by + using this function. + + +2.4.2. Argument Processing + + * char *one_argument (char *argument, char *first_arg); + * char *two_arguments (char *argument, char *first_arg, char *second_arg); + * char *any_one_arg (char *argument, char *first_arg); + + These functions are frequently used in MUD commands to parse the + arguments to those commands. As their names imply, one_argument() + will peel off one argument from the string given by the user while + two_arguments() will peel off two at a time. Note that these + functions ignore (and will not return) words such as: "in", "from", + "with", "the", "on", "at", and "to". This is so the commands do not + need to know the difference between "put the sword in the bag" and + "put sword bag". If those words are really needed for the command, + then use any_one_arg() instead. It works just like one_argument() in + all other respects. All of these functions convert the peeled off + argument(s) to lower case as part of the process of storing them in + the user-supplied buffer. + + * char *one_word (char *argument, char *first_arg); + + Peels an argument off from a string like one_argument, but respects + grouping via quoting. If the user supplies, '"moby dick"', + one_argument() would return an argument of '"moby', while one_word() + would return an argument of 'moby dick'. This function converts the + peeled off argument(s) to lower case as part of the process of storing + them in the user-supplied buffer. + + * void half_chop (char *string, char *arg1, char *arg2); + + Apparently a DikuMud relic. Instead of returning the leftover + argument bits in the return value, this copies the result (sans + leading spaces) into a second buffer. + +2.4.3. Character Output (Hello, world!) + + * void log (const char *format, ...); + + Whenever a piece of information needs to be sent to the MUD's logs, + this is the function to use. It's especially useful for debugging + and supports variable arguments like the printf() and sprintf() + functions. To prevent compilation errors due to a conflict with C's + natural logarithm function of the same name, 'log' is actually an + alias for this function's real name, basic_mud_log(). + + * void mudlog (const char *str, int type, int level, bool file); + + In most cases mudlog() is better than log() because it announces to + both the immortals on the MUD and, optionally, the file logs. Chances + are the immortals will notice something faster while logged in to the + game than in the system logs. + + * void send_to_char (struct char_data *ch, const char *messg, ...); + + This is the game's tether to the players; its mouth; its voice. Most + game output goes through this function so it is used very frequently. + It supports variable argument formatting like the C library's + printf() and sprintf() functions. + + * void act (const char *str, bool hide_invisible, + struct char_data *ch, struct obj_data *obj, + const void *vict_obj, int type); + + When dealing with character interactions there are frequently three + situations to cover: the actor, the target, and the observers. This + function takes care of such output along with handy cases for + his/her, he/she/it, and other language special cases. + + See the 'act()' documentation (act.pdf) for more information on + what each particular parameter is used for. + + * void page_string (struct descriptor_data *d, char *str, + bool keep_internal); + + Places the character into a pageable view of whatever string is + given to it. Handy for long board messages, opening announcements, + help text, or other static information. + +2.4.4. File Input + + * int get_line (FILE *fl, char *buf); + + Reads one or more lines, if possible, from the given file handle + into a user-supplied buffer. This skips lines that begin with an + asterisk (*), which are considered to be comments, and blank lines. + The returned line has the line terminator(s) removed, and the total + number of lines read to find the first valid line is returned. A + value of zero indicates that an error occurred or that end of file + was reached before a valid line was read. + + * int get_filename (char *orig_name, char *filename, int mode); + + Fills in the 'filename' buffer with the name of a file of type + 'mode' for a player with name 'orig_name'. The mode parameter can + be one of: + + * CRASH_FILE, for player object files, + * ALIAS_FILE, for player aliases, + * ETEXT_FILE, for the unimplemented e-text system. + + The returned filename contains a path to a file in a directory based + upon the file type and the first letter of 'orig_name'. + +2.4.5. Utility Functions + + * int rand_number (int from, int to); + + Rolls a random number using a (pseudo) random number generator in + the range [from, to]. The random number generator is seeded with + the time CircleMUD booted as returned by the time() system call. + This provides a good, difficult to predict sequence of numbers. + + * int dice (int num, int size); + + Simulate rolling 'num' dice, each with 'size' sides, and return the + sum of the rolls. + + * size_t sprintbit (bitvector_t bitvector, const char *names[], char *result, size_t reslen) + + Treat an array of strings as if they were descriptions for the + individual bits in 'bitvector' and create a string describing it. + This is used by the wizard function do_stat() to give + human-readable output to the various bitvectors used as storage by + the code. This is the approximate reverse of "PRF_LOG1 | PRF_DEAF", + for example. + + * size_t sprinttype (int type, const char *names[], char *result, size_t reslen) + + Retrieves a value from an array of strings. The difference between + this and "array[number]" is that this will avoid reading garbage + values past the end of the array. sprinttype() assumes the string + arrays are terminated by a "\n" entry. + + * int search_block (char *arg, const char **list, int exact) + + Searches an array of strings for a match to 'arg' and returns the + index in the array of the match, or -1 if not found. If 'exact' is + false, then a prefix match is done akin to is_abbrev(). This is + useful to map symbolic names to numerical constants. Think of it + as the opposite of array[number]: "What index has this value?" + +2.4.6. Character/Object Manipulation + + * void char_to_room (struct char_data *ch, room_rnum room); + * void char_from_room (struct char_data *ch); + + Reciprocal, low-level functions to put a character into and remove a + character from a given room. The room number must be specified with + a "real number" (an index into the room tables) as returned by + real_room(). Since a character can only be in one room at a time, + you must call char_from_room() to remove a character from his + current location before placing him in another. After a + char_from_room() call, the character is in NOWHERE and must be moved + to another room using char_to_room(). + + These functions do not check if the character is allowed to enter or + leave the room; nor do they provide output to indicate that the + character is moving. + + * void extract_char (struct char_data *ch); + + Remove the character from the game world and then frees the + memory associated with it. Players are saved before removal. Any + objects still on the character are dumped on the ground. + + * void equip_char (struct char_data *ch, struct obj_data *obj, int pos); + * struct obj_data *unequip_char (struct char_data *ch, int pos); + + Takes a free-floating object (i.e., not equipped, in inventory, on + the ground, or in another object) and equips it to the character + for the specified location. unequip_char() does the opposite; it + removes the object from the character's equipment list and returns + it as a free-floating object. The object being unequipped must be + placed elsewhere or destroyed. Note that some objects may not be + equipped by characters of certain classes and/or alignments. + + * void obj_to_char (struct obj_data *object, struct char_data *ch); + * void obj_from_char (struct obj_data *object); + + Reciprocal, low-level functions to put an object into and remove an + object from a given character's inventory. Since an object can only + be in one location at a time, you must use one of the obj_from_X() + functions to remove it from its current location before using + obj_to_char() to place it in someone's inventory. After an + obj_from_char() call, the object is in NOWHERE and must be moved to + another location using one of the obj_to_X() functions. + + These functions do not check if the character is allowed to carry or + discard the object; nor do they provide any output to inform anyone + that the character has received or discarded the object. + + * void obj_to_obj (struct obj_data *object, struct obj_data *cont); + * void obj_from_obj (struct obj_data *object); + + Reciprocal, low-level functions to put an object into and remove an + object from a given container object. Since an object can only be + in one location at a time, you must use one of the obj_from_X() + functions to remove it from its current location before using + obj_to_obj() to place it within another object. After an + obj_from_obj() call, the object is in NOWHERE and must be moved to + another location using one of the obj_to_X() functions. + + These functions do not check if the container is allowed to carry or + discard the object; nor do they provide any output to inform anyone + that the container has received or discarded the object. + + * void obj_to_room (struct obj_data *object, room_rnum room); + * void obj_from_room (struct obj_data *object); + + Reciprocal, low-level functions to put an object into and remove an + object from a given room. Since an object can only be in one + location at a time, you must use one of the obj_from_X() functions + to remove it from its current location before using obj_to_room() to + place it in a room. After an obj_from_room() call, the object is in + NOWHERE and must be moved to another location using one of the + obj_to_X() functions. + + These functions do not check if the room is allowed to contain or + discard the object; nor do they provide any output to inform anyone + that the room has received or discard the object. + + * void extract_obj (struct obj_data *obj); + + Removes the object from its place in the world, then destroys it. + +2.4.7. Object Locating + + * struct obj_data *get_obj_in_list_num (int num, struct obj_data *list); + + Get an object from 'list' with the specified real object number. + Only takes first object; no "2.bread" support. + + * struct obj_data *get_obj_num (obj_rnum nr); + + Find the first object in the world with the real object number + given. Does not have "2." support. + + * struct obj_data *get_obj_in_list_vis (struct char_data *ch, char *name, + int *number, struct obj_data *list); + + Find the 'number'-th object in 'list' with keyword 'name' that the + character can see. A NULL is returned on failure to locate such an + object, or if not enough objects to satisfy 'number' were found. + 'number' is a pointer to an integer so it can be decremented when + doing multiple searches, such as room then world. If the first + object is desired, 'number' is left NULL. + + * struct obj_data *get_obj_vis (struct char_data *ch, char *name, + int *number); + + Find the 'number'-th object in the world with keyword 'name' that + the character can see. A NULL is returned on failure to locate + such an object, or if not enough objects to satisfy 'number' were + found. 'number' is a pointer to an integer so it can be decremented + when doing multiple searches, such as room then world. If the + first object is desired, 'number' is left NULL. + + * struct obj_data *get_obj_in_equip_vis (struct char_data *ch, char *arg, + int *number, struct obj_data *equipment[]); + + Find the 'number'-th object in the character's equipment list with + keyword 'name' that the character can see. A NULL is returned on + failure to locate such an object, or if not enough objects to + satisfy 'number' were found. 'number' is a pointer to an integer so + it can be decremented when doing multiple searches, such as + equipment then inventory. If the first object is desired, 'number' + is left NULL. + + * int get_obj_pos_in_equip_vis (struct char_data *ch, char *arg, + int *number, struct obj_data *equipment[]); + + Return the index of the 'number'-th object in the character's + equipment list with keyword 'name' that the character can see. A + -1 is returned on failure to locate such an object, or if not + enough objects to satisfy 'number' were found. 'number' is a + pointer to an integer so it can be decremented when doing multiple + searches, such as equipment then inventory. If the first object is + desired, 'number' is left NULL. + + * int generic_find (char *arg, bitvector_t bitvector, + struct char_data *ch, struct char_data **tar_ch, + struct obj_data **tar_obj); + + Searches any or all of the character's equipment, inventory, + current room, and world for an object with the keyword given in + 'arg'. A 2nd or 3rd object is denoted in "2.object" notation. The + function's return value specifies where the object was found, or 0, + and the 'tar_obj' value is updated with the object itself, or NULL. + NOTE: This also does characters, either separately or + simultaneously. + +2.4.8. Character Locating + + * struct char_data *get_char_room (char *name, int *number, room_rnum room); + + Find the 'number'-th character in the room with the keyword 'name'. + A NULL is returned on failure to locate such a character, or if not + enough characters to satisfy 'number' were found. 'number' is a + pointer to an integer so it can be decremented when doing multiple + searches, such as room then world. If the first character is + desired, 'number' is left NULL. + + * struct char_data *get_char_num (mob_rnum nr); + + Find the first mobile in the world with real mobile number given. + This does not have support for "2." notation. + + * struct char_data *get_char_room_vis (struct char_data *ch, char *name, + int *number); + + Find the 'number'-th character in the room with the keyword 'name' + that is visible to the character given. A NULL is returned on + failure to locate such a character, or if not enough characters to + satisfy 'number' were found. 'number' is a pointer to an integer + so it can be decremented when doing multiple searches, such as room + then world. If the first character is desired, 'number' is left + NULL. + + * struct char_data *get_char_world_vis (struct char_data *ch, char *name, + int *number); + + Find the 'number'-th character in the world, searching the + character's room first, with the keyword 'name' that is visible to + the character given. A NULL is returned on failure to locate such a + character, or if not enough characters to satisfy 'number' were + found. 'number' is generally a pointer to an integer so it can be + decremented when this does both searches. If the first character is + desired, 'number' is left NULL. + + * struct char_data *get_char_vis (struct char_data *ch, char *name, + int *number, int where); + + When 'where' is FIND_CHAR_WORLD, call 'get_char_world_vis()'. If + 'where' is FIND_CHAR_ROOM, call 'get_char_room_vis()'. Otherwise, + return NULL. This is kept for compatibility with various calls in + the source code or if people want to easily change a search based + on a variable. + + * int generic_find (char *arg, bitvector_t bitvector, + struct char_data *ch, struct char_data **tar_ch, + struct obj_data **tar_obj); + + Searches the character's current room and/or world for a character + with the keyword given in 'arg'. A 2nd or 3rd character is denoted + in "2.character" notation. The function's return value specifies + where the character was found, or 0, and the 'tar_ch' value is + updated with the character itself, or NULL. NOTE: This also does + objects, either separately or simultaneously. + +2.4.9. Violence + + * void set_fighting (struct char_data *ch, struct char_data *victim); + + Initiates fighting between 'ch' and 'victim'. + + * void stop_fighting (struct char_data *ch); + + Removes the character from a fighting posture. Note that if an + enemy is still considered fighting this character, the character + will revert back to fighting as soon as the enemy hits them again. + + * void hit (struct char_data *ch, struct char_data *victim, int type); + + Makes the character attempt to hit the victim. The type determines + if it is a skill, backstab in particular, or other type of damage + to attempt to hit with. The type is generally left as + TYPE_UNDEFINED to use the character's natural type. + + * int damage (struct char_data *ch, struct char_data *victim, int dam, + int attacktype); + + Cause bodily harm to the victim, courtesy of the character. The + damage and attacktype determine the message reported. Immortals + and shopkeepers (that aren't charmed) may not be injured. Damage + is capped at 100 per hit. + 3. Adding Features + 3.1. Adding Commands + + In the course of writing new functionality for your MUD, one of the +first projects you may try is to add your own command. Some commands, like +socials, are special, but most commands will require you to implement some +method to manipulate and parse the user's input. In CircleMUD, this is done +with 'command functions' which have a special declaration form: + + ACMD(do_/* Command name. */) + { + . + . /* Command code. */ + . + } + + The command functions are then registered with the command +interpreter by adding them to the cmd_info[] table in interpreter.c. The +order within the command table is significant; entries at the top are +substring matched prior to entries lower in the list. So if 'kill' is +before 'kiss', then 'ki' will match 'kill'. Something else to be aware of +is that this can render commands unreachable, such as 'goad' being before +'go'. The 'go' can never be matched because 'goad' will always have a valid +substring matched first. + + The fields of importance are: + + * const char *command + The name of the command being registered. Remember the substring + matching when deciding upon the order in the table. + + * byte minimum_position + One of the POS_xxx constants #define'd in structs.h. This enforces + the minimum position, inclusive, the user must be in, in order to + execute the command. + + * ACMD(*command_pointer) + This is the name of the function the command will call. It must be + defined in the code with the ACMD() macro and prototyped above the + command table itself. + + * int minimum_level + The minimum level, inclusive, the user must be to execute the + command. + + * int subcmd + To allow the same code function to handle multiple, similar + commands, this field allows an identifying number to be given to + the command's function. + + The ACMD declaration form is a C macro that sets up the command +function to receive the right arguments. All command functions in CircleMUD +receive the same set of parameters: + + * struct char_data *ch + + The character that issued the command (or, perhaps, was forced to + issue the command): the actor. + + * const char *argument + + A string that contains the arguments the user gave to the command, + with any leading spaces from the command interpreter still intact. + For example, if the user typed "tell ras Hello, there" at the + prompt, argument would be " ras Hello, there". This string is + typically processed using one or more of the functions from Sections + 2.4.1 (Basic String Handling) or 2.4.2 (Argument Processing). + + * int cmd + + The index within the command table where this command was found. + Useful when multiple commands can invoke the same command function + and you want to identify which command was issued. Since command + indices can change when you add new commands, the primary use of + this field is in special procedures and with the IS_CMD macro. Do + not confuse this with the subcmd parameter, which is more general + purpose. + + * int subcmd + + A special, user-defined integer value passed to select a + "subcommand." Usually zero, but sometimes used when multiple + commands with similar behavior are implemented with a single command + function. Since the subcmd's value is supplied within the command + table and has a meaning determined entirely by the command's author, + it will not change when you add new commands. + + A command with no arguments is very simple to write. For example, +here's a simple command that sends a nice, personalized greeting to the user +when she runs it: + + ACMD(do_hello) + { + act("Hello, $n.", FALSE, ch, NULL, NULL, TO_CHAR); + } + + To allow the user to access this command, you have to add it to the +command table as discussed before. This can be done by adding the ACMD +prototype above cmd_info[] (if necessary) in interpreter.c, like + + ACMD(do_hello); + +and then adding to cmd_info[] the command's information, as previously +discussed: + + { "hello", POS_DEAD, do_hello, 0, 0 }, + + Our information specifies this is a command named "hello" which +anyone can use, regardless of their position (since dead is the minimum) or +level (since 0 is the minimum), calls the do_hello() function to be +executed, and has no subcmd. Note that because cmd_info[] does not encode +information about the arguments to the command, we don't need to do anything +different for commands that take arguments. + + Since the command interpreter doesn't process arguments for us +(allowing cmd_info[] to be simple and general), we have to process them +ourselves. Suppose we want to update our "hello" command to send a greeting +to other users, when its invoked with an argument. First, we need to get +the first argument (if any) by using the one_argument() function. In order +to do this, we need a place to store the first argument (if any), which we +declare as a local character buffer of length MAX_INPUT_LENGTH. (Note that +regardless of what length we *expect* the argument to be, we always make our +buffer the maximum input size, so users cannot overflow the buffer and crash +the game.) + + After this, we need to see what the argument is. If it's nothing, +we'll default to our old behavior of just saying hello to our user. +Otherwise, we need to look up the character with the given name. If we +can't find anyone by that name, we send an error message. If we find +someone, we send the greeting to the character we found. We check if the +given argument is empty by seeing if its first character is C's end of +string marker ('\0' or 0). Since one_argument() strips leading spaces for +us, we don't have to worry about them. If the string is not empty, we need +to look up a character in the current room by that name, using +get_char_vis() (see Section 2.4.8. Character Locating). + + Our changes give us: + + ACMD(do_hello) + { + char arg[MAX_INPUT_LENGTH]; /* First argument. */ + struct char_data *targ; /* Who to greet? */ + + one_argument(argument, arg); + + if (!*arg) { /* Common idiom for empty string test. */ + act("Hello, $n.", FALSE, ch, NULL, NULL, TO_CHAR); + return; /* We're done for this case. */ + } else if (!(targ = get_char_vis(ch, arg, FIND_CHAR_ROOM))) { + send_to_char(ch, NOPERSON); + return; /* Done for this case. */ + } + + /* Otherwise we got a target: */ + act("You greet $N.", FALSE, ch, NULL, targ, TO_CHAR); + act("$n greets you.", FALSE, ch, NULL, targ, TO_VICT); + act("$n greets $N.", FALSE, ch, NULL, targ, TO_NOTVICT); + } + + NOPERSON is a constant defined in config.c for use an error message +when the target of a command cannot be found. Other such constants are OK +(for when everything goes well) and NOEFFECT (as a general failure message +for skills, spells, and commands whose success rely on chance). + + Of course, this command is little more than an overcomplicated +social (we'll see in the next section that socials don't require command +functions at all). Doing something of interest is up to you, as the +programmer. Our "hello" command only serves as a starting point and +demonstration of some idioms used throughout CircleMUD. + + 3.2. Adding Socials + + Socials are commands that only write messages to the user and +possibly his room and/or an optional victim. Examples are typical mud +commands like "nod" or "wave", to let players perform various demonstrative +actions. Our "hello" command above is an example. However, since socials +are very common and superficially simple, there's a simplified way to write +them. + + The lib/misc/socials file in the CircleMUD directory contains the +actual socials in the following format: + + + + + + + + + + + + + The exact meaning and format of these fields is described in +'socials.pdf', although much of it corresponds to act() (like the hide flag +or the format of the messages) and the command interpreter (like the command +name and the minimum position the *victim* must be in for the social to +work). + + Programmatically, the social is still a command and must be +registered in cmd_info[] like any other command. All socials have the same +command function, do_action(), which does the specialized social system +processing. Thus, to add a social "foobar" to the command interpreter, we +add the following line to the appropriate place in cmd_info[] (taking into +account that order is significant, as discussed in Section 3.1): + + { "foobar", POS_RESTING, do_action, 0, 0 }, + + Note that the second element in the table entry is the minimum +position the user of the social must be in, while the lib/misc/socials file +stores the minimum position that the *victim* (if any) must be in for the +social to work. In this case, the command can only be used by people that +are awake and fully conscious (resting, sitting, or standing). + + 3.3. Adding Spells + + CircleMUD improves greatly over standard Diku handling for spells, but +how you go about adding them depends on the type of spell you want to make. +Damage, affection, group, mass area, area, monster summoning, healing, status +removal, and item enchanting spells are all generated in a template format +with a touch of special messages and coding effects. More complicated spells +such as 'locate object', 'summon', or 'identify' are a combination of the +behavior of spells and commands. They are spells in the sense the code checks +for mana and requires the 'cast' syntax but are also commands in the sense +that beyond the basic handling, the spell is implemented as a subroutine with +given parameters. + + All spells require a definition to determine the amount of mana used, +how the spell behaves, and what the spell is named. To do that, the function +spello() is called from mag_assign_spells() in spell_parser.c. It is called +as: + + spello( + unique_spell_number = ID# from 0 .. TOP_SPELL_DEFINE for this spell. + spell_name = Name to be used for 'cast' command. + max_mana = Mana cost of spell when first learned. + min_mana = Minimum mana cost to ever cast the spell. + mana_change = Reduction in mana cost per level beyond learning it. + minimum_position = Whether castable sitting, standing, fighting, etc. + valid_targets = If the spell targets people, yourself, items, etc. + offensive = Whether casting on someone else is a hostile action. + spell_routines = One or more of the magic template classifications. + wear_off_message = Text to display when the spell wears off, or none. + ) + +A spell with a minimum position value of 0 may not be cast by mere mortal +players. For example, the spell "armor" is described as: + + spello(SPELL_ARMOR, "armor", 30, 15, 3, POS_FIGHTING, + TAR_CHAR_ROOM, FALSE, MAG_AFFECTS, + "You feel less protected."); + +This spell costs 30 mana at the first level it is learned and decreases in +cost by 3 mana per level afterward until it reaches the minimum of 15. The +spell may be cast either fighting or standing, but not sitting, resting, +sleeping, or otherwise incapacitated. Armor will target anyone in the current +room as a non-hostile action. It processes through MAG_AFFECTS so there will +be a lingering affection, after which the "You feel less protected" message +will display. + + To allocate a new spell, create a new #define symbol in spells.h in +the same pattern as the others there. In that header, give the new spell an +unused spell number equal to or less than MAX_SPELLS. If you run out of spell +slots then other means not covered here will be necessary to add more spells. + +3.3.1. Template Spells + + Similar types of spells have generalized routines that handle multiple +spells with very little different code. A damage spell is a damage spell is a +damage spell. Even if a spell does multiple actions, such as blinding + +damage + monster summon, the damage portion of the spell acts identical to a +spell that simply does damage. The only difference is how much it does and +whether there are special mitigating factors. For example, 'chain lightning' +in mag_damage() (since it is a MAG_DAMAGE spell) is simply: + + case SPELL_CALL_LIGHTNING: + dam = dice(7, 8) + 7; + break; + +So the spell does 7d8+7 damage. Simple enough. All checking for saving +throws, valid targets, proper mana reserves, etc. is all handled by the +generic code with a bit of definition for the code to operate by. + + The code fragment in the template sections can use any information +about the caster, target, or environment that it chooses to modify the damage, +success, or effect done to the target. Some spells do more damage if the +caster is a magic user. Others might outright kill lower level targets but +only slightly wound more experienced ones. The effect is up to you. + + Affection spells require more in their fragment than the simple damage +spells. They create affection structures that are then given to the target of +the spell for their specified duration if the spell succeeds. More than one +affection can be given by a single spell, as shown below in "bless": + + case SPELL_BLESS: + af[0].location = APPLY_HITROLL; + af[0].modifier = 2; + af[0].duration = 6; + + af[1].location = APPLY_SAVING_SPELL; + af[1].modifier = -1; + af[1].duration = 6; + + accum_duration = TRUE; + to_vict = "You feel righteous."; + break; + + Any modifier listed in structs.h in the APPLY_xxx section may be used +as the location field. The modifier's effect will depend on the affection +type used. Up to MAX_SPELL_AFFECTS values can be assigned to. Although not +listed in the above example, a '.bitvector' value may be assigned to if the +spell should tag the player with an AFF_ flag. If multiple castings of the +same spell should be cumulative in duration, the 'accum_duration' variable is +set to TRUE. Likewise, if the modifier is cumulative, the 'accum_effect' +variable should be set to TRUE. A string assigned to 'to_room' will be passed +through act() for the occupants of the same room as the caster. A 'to_vict' +string will be given to act() with the target of the spell as the recipient of +the message. + + Group spells simply call another spell on everyone in your current +group. If you want a 'group fly' spell, then you make a 'fly' spell first. +Afterward, you make the 'group fly' definition and then fill in some template +areas of the perform_mag_groups() function. What you write there will depend +on how your spell is designed. + + General summoning spells (not 'summon' itself) deal with the +conjuration of mobiles. They require: 'fmsg', a failure message array index +number; 'mob_num', the virtual mobile number to summon; 'pfail', the percent +chance of failure; and 'handle_corpse', mostly for the "animate dead" spell so +it can move the items from the corpse being animated to the mobile being +summoned. These spells lend themselves to more customization than some of the +other types. + + Healing spells in mag_points() can restore either health or movement +points by default. Just assign the amount of health healed to a 'healing' +variable, the amount of movement points restored to a 'move' variable, and +send the target a message with send_to_char(). The general code will handle +updating the character's attributes, position, and make sure a dying character +is restored to normal functions if healed sufficiently. + + Unaffection spells revert the effects of other spells, such as +"blindness", "silence", or "drunken stupor." There are only three variables +used in mag_unaffects(): to_vict, to_room, and spell. The important variable +is 'spell', which determines which spell effect this unaffection spell will +counter. The 'to_vict' and 'to_room' messages are optional but sent to the +victim and room, respectively, if provided. + + Object alteration spells deal with magical modifications to items, +such as poisoning, cursing, enchanting, or making them invisible. These +spells are all unique by nature so only 'to_char' and 'to_room' are expected +to be set, as messages to the character and room, respectively. If 'to_char' +is left NULL, it is assumed the spell failed and a "no effect" message is +given. + + A creation spell conjures an item out of nothingness. The only +variable expected is 'z', which specifies the object virtual number that +should be created. Note that only a single object is created and there is no +current mechanism for making multiples. + + The last function of note, mag_materials(), is not a spell type at all +but a helper function which can be used to require up to 3 spell reagents for +a particular spell to be cast. The function will return TRUE if the caster +has the objects, otherwise FALSE. If the 'extract' variable is TRUE, then the +objects in question will be consumed by the casting. You can also make the +function 'verbose', but it more of a debugging/funny option than practical. + +3.3.2. Manual Spells + + Any spell that doesn't fit one of the template molds is implemented as +a manual spell. Adding a manual spell requires a function to be written, +generally in spells.c, with the ASPELL() macro. After the requisite spell +identifier macro is added to spells.h, add it to the manual spell list in +spell_parser.c, call_magic(). (Search for "MANUAL_SPELL".) + +Manual spells are given: + + level + The effective character level of the spell being cast. This is NOT + the same as the level of the character because the spell could have + been case by a wand, staff, or scroll instead of the character. + ch + The character causing the spell. + victim + The target of the spell, if a character. + obj + The target of the spell, if an object. + +Other than that, manual spells can do anything. Think of them as being +similar to standard commands in power and scope. A useful modification is to +add 'argument' support to spells so that "locate object" works properly and a +"change weather" spell could make it "better" or "worse." + + 3.4. Adding Skills + + Skills in CircleMUD are usually implemented as commands. The first +steps to adding a skill are similar to those of adding a spell. First, make +sure you have a clear idea of what your skill is going to do, who you're +going to give it to, and how it fits in with the rest of the game. Try to +avoid making too many skills that do basically the same thing -- having lots +of skills isn't a meaningful feature if most of them can be ignored. + + After you have a good idea of what you want to do, why you want to +do it, and why it's a good idea to do it, then start by adding a SKILL_xxx +#define to spells.h and the corresponding skillo() line to +mag_assign_spells() in spell_parser.c. The skillo() function takes, as its +first argument, the SKILL_xxx #define and, as its second, the name of the +skill, as a string. This registers the skill as something that can be +practiced. As with spells, you have to register the skill's availability +with individual classes at the appropriate levels in the init_spell_levels() +function of class.c. + + Now your skill can be gained and practiced by players of an +appropriate level and class, but it doesn't actually do anything. Most +skills, like "bash" and "kick", are simply commands that perform skill +checks. The setup and everything else is the same as in Section 3.1, Adding +Commands. The body needs to account for (1) whether the command's user can +access the skill and (2) whether they were successful in using the skill. +For (1), CircleMUD uses the idiom + + if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_xxx)) { + send_to_char(ch, "You have no idea how.\r\n"); + return; + } + +to check if the skill is available. The GET_SKILL macro returns the +proficiency (as a percentage) the given character has in the given skill. +If the proficiency is 0%, the player does not have the skill (either because +his class doesn't have it or he's not learned it, yet). This check is +preferred over directly testing if the player is of the right class(es) to +use the skill, since that would require you to change several functions +across several files to give skills to other classes (instead of just being +able to add spell_level() calls in class.c). + + At this point you would do argument processing in the typical +manner, as well as any other checks that are necessary (for instance, you +might want to check if the room is peaceful, as done in do_bash() in +act.offensive.c). Last, you want to check for the success or failure of the +skill by rolling a percentage to compare against the user's proficiency +(probability of success). This is typically done with: + + if (number(1, 101) > GET_SKILL(ch, SKILL_xxx)) { + /* Failure. */ + } else { + /* Success. */ + } + +where you'd replace the comments with the relevant failure or success code. + + For skills that do damage, like "bash" and "kick", the messages for +success and failure are typically not encoded in the skill itself, but +instead as damage messages in lib/misc/messages, which has the format: + + M + + + + + + + + + + + + + + +The skill number is the #define as appears in spells.h and the rest are +single line messages that will be passed to act(). This is similar in many +respects to lib/misc/socials. The format is discussed in more detail in a +comment at the beginning of lib/misc/messages. + + These messages are then displayed by calling damage() with the +appropriate arguments, as discussed in Section 2.4.9, Violence, with the +attacktype argument set to the SKILL_xxx #define, as in + + /* + * See above and Section 3.1, Adding Commands: + * ... skill checks, argument processing, etc. + * ... vict is set to skill's victim. + */ + + if (number(1, 101) > GET_SKILL(ch, SKILL_FOO)) { + /* Failure means 0 damage is done. */ + damage(ch, vict, 0, SKILL_FOO); + } else { + /* Success means we do some damage. */ + damage(ch, vict, 10, SKILL_FOO); + } + + Note that even when the skill succeeds and, thus, our call to do 10 +damage to the victim of the skill is made, we're not guaranteed to do the +damage. The hit may miss, in which case damage() returns 0. Additionally, +the hit may kill the victim, in which case damage() returns -1. If we're +going to be modifying vict in our skill's function after the call to +damage(), it's important to take these return values into consideration. +See do_bash() in act.offensive.c. + + 3.5. Adding Classes + + Classes are one of the implementors' most important decisions because +the players will constantly deal with them, their advantanges, and their +limitations. A good class should be balanced so that it has its own unique +perks and flaws, never making other classes pointless to play. + + Most changes to be done for classes will be found in the class.c file. +There may be special quirks for classes implemented in the other files but the +basic defining characteristics are all there. The class needs a name, +abbreviation, menu entry, unique class number, skill list, guild information, +saving throws, combat hit probabilities, an ability priority list, +hit/mana/move advancement per level, basic starting kit, opposing item flags, +spells, skills, experience charts, and level titles. It's an exhaustive list, +but the actual addition of a class isn't nearly as complicated as it sounds. + + The first change for a class required external of class.c is in +structs.h. There, search for CLASS_UNDEFINED and add a new CLASS_xxx +definition for your class name with the next available number. Remember to +bump the value of NUM_CLASSES, just below, by 1. + + Then search structs.h for "Extra object flags" so you can add an +"ITEM_ANTI_xxx" flag for your new class. As before, use the next available +number in the sequence for ITEM_xxx flags. Note that the limit is "(1 << 31)". +Beyond that you'll need special changes (not covered here) to add more flags. + + The "ITEM_xxx" extra flags have a corresponding text description in +constants.c, so search it for "ITEM_x (extra bits)". Add a string giving a +short name for the new ITEM_ANTI_xxx flag, in the appropriate order, before +the "\n" entry near the bottom. + + The shops have a similar "don't trade" setup, so search shop.h for +"TRADE_NOGOOD" to add a new TRADE_NOxxx item to the list for the class to be +added. Below that (near "NOTRADE_GOOD"), a line will need to be added for +each new class so the 'no trade' status of a shop can be tested for the class. + + With the definitions in shop.h, the shop code in shop.c can then be +modified to take into account the new classes. In a manner similar to +constants.c, there's a table in shop.c with textual names for the TRADE_NOxxx +values. Add the new class names to the "trade_letters[]" array in the same +order as the TRADE_NOxxx bits were added to shop.h. Also in shop.c, the +is_ok_char() function will need modified to add "IS_xyz(...) && +NOTRADE_xyz(...)" conditions, to make the above changes take effect. + + Lastly for changes beyond class.c, search utils.h for "IS_WARRIOR" and +make a similar definition below it for the new class. + + Most of the changes to class.c will be straight-forward if going by the +existing classes, so only a few items of note: + + 1) The letters used in parse_class() must be unique and should + correspond to the highlighted characters in the 'class_menu' variable. + + 2) Lower saving throw values are better. + + 3) Lower 'thaco' values are better. + + 3.6. Adding Levels + + Some people feel the standard 34 levels aren't enough and want to +add more. Others feel 34 is too many and want to reduce it. Fortunately, +changing the number of levels in the MUD is fairly painless. There are +only three important things to remember: adjusting the secondary tables to +match your new levels, making over 127 levels requires some additional +modifications, and to readjust the mobiles afterward. + + The secondary functions that rely on levels directly are: +saving_throws, thaco, backstab_mult, level_exp, title_male, and +title_female. These must be changed to correctly cover the entire range of +new levels for the MUD. If not, the missing levels will have incomplete +data and may act in unexpected ways. Fortunately, you'll receive error +messages in the logs if such an event happens. + + As the number of mortals levels is always one less than the lowest +immortal level, changing LVL_IMMORT in structs.h to a new value will give +the desired change. Make sure you change the functions described above at +the same time. The other immortals levels should be adjusted accordingly. + + If you're making more than 127 total levels on the MUD, a little +structs.h surgery is required. The default range on the 'level' variable +is -128 to 127. CircleMUD doesn't actually use negative levels so changing +it to 'ubyte level' will allow 255 levels. Note that this setting hasn't +been tested so test your new level settings to make sure they work as +expected. If you need more than 255 levels, you'll need to change the +'byte' to something larger, like ush_int (65,535) or unsigned int (4.2 +billion). Changing the variable type beyond byte will result in the +erasing of your player files and require changes elsewhere in the code +where levels are manipulated. + + Once you've changed the number of levels on your MUD, the +implementor character you may have already created will now have the wrong +level to be an implementor. If you've decreased the levels then a 'set +self level XX' command should work to drop yourself to the proper level, +since you're considered above the new implementor level still. Those +increasing the number of levels will find their implementor is now likely +considered a mortal. In that case, you can either erase the player files +to recreate yourself or make a command to make yourself the proper level, +such as: + + ACMD(do_fixmylevel) + { + if (GET_IDNUM(ch) == 1) + GET_LEVEL(ch) = LVL_IMPL; + } + + Now remember to change all the mobiles too so they have proper +levels. If you added levels, it'll make the mobiles weaker unless fixed. +If reducing the levels, then you'll end up with error messages in the logs +when those mobiles try to use saving throws or other level-dependent +values. + + 3.7. Adding Color + Color in CircleMUD is handled on a varying scale of color levels +the player can choose to display. The possible levels are off, sparse, +normal, and complete. If a player has color off, no color must be sent. + + To send color to the players, use the CC* family of macros: + + CCNRM: Normal text color, as defined by player's terminal. + CCRED: Red + CCGRN: Green + CCYEL: Yellow + CCBLU: Blue + CCMAG: Magenta + CCCYN: Cyan + CCWHT: White + +Each macro takes a pointer to the character and the level at which the +color given should be displayed. If the player uses a lower level of color +than given to the macro in the code, the color code will reduce to an empty +string so it does not appear. See 'color.pdf' for more information on this +process. + + Now suppose you wish to add high-intensity colors, blinking, or +backgrounds for your text. The place to look for the existing color codes +is in screen.h, but you'll just see codes like "\x1B[31m" there. So what +is "\x1B[31m"? It's an ANSI color code understood by various terminal +emulations to display color. There are predefined colors for each code and +a special format to use so you can't just make up codes and expect them to +work. + + In order to compare the low-intensity colors with the +high-intensity, an additional color must be known to complete the pattern, +black: + + #define BBLK "\x1B[30m" + +The terminal background color is assumed black by CircleMUD so that +particular color definition isn't in screen.h. Now a comparison of red and +green with their bright counterparts: + + #define KRED "\x1B[31m" (Dark) + #define BRED "\x1B[0;1;31m" (Bright) + + #define KGRN "\x1B[32m" (Dark) + #define BGRN "\x1B[0;1;32m" (Bright) + +If you want the bright colors, you can extend this pattern to get the other +standard colors. + + Once the #define is in screen.h, it needs to be usable via the CC* +color convention to respect the color level of the players, so for every +new color code add a CC* for it, such as: + + #define CCBRED(ch,lvl) (clr((ch),(lvl))?BRED:KNUL) + #define CCBGRN(ch,lvl) (clr((ch),(lvl))?BGRN:KNUL) + + With a number of colors, making a new CC* code for each one may get +tedious. You may want to add a new macro, such as: + + #define CC(ch, color, lvl) (clr((ch),(lvl))?(color):KNUL) + +Then you can use CC(ch, KRED, C_NRM) instead of CCRED(ch, C_NRM). Whether +or not you want to use this idiom is up to you. It might come in handy +once you get into blinking (use sparingly!) and background colors. The +background colors are "\x1B[40m" (black), "\x1B[47m" (white), and +everything in the middle as per the foreground colors. + 4. Writing Special Procedures Special procedures are the way to give life to your world. Through special procedures you can, for instance, make Mobiles react to player -actions, fight inteligently,etc. +actions, fight intelligently,etc. + Using special procedures, your virtual world is not just a bunch of monsters, objects and rooms, reduced to a number of statistics. Just like good descriptions add atmosphere to the world, good use of special procedures adds flesh and life to the world. - Several special procedures are provided with stock CircleMud which you + + Several special procedures are provided with stock CircleMUD which you can use to create your own and get used to the mechanics of special procedures. These special procedures can be found in castle.c and spec_procs.c. They range from very simple procedures, like puff (pulsed special procedure) or bank (command-driven special procedure), to very -complex procedures like the guildmaster. +complex procedures like the guild master. + In this chapter, false refers to the value 0 and true to any non-zero value. + 4.1. Overview of Special Procedures Special procedures are nothing more than C functions, which are associated to mobiles, objects and rooms. + In the standard version of CircleMUD, special procedures are defined and assigned at compile time and cannot be changed or reassigned during runtime. + 4.2. Pulsed vs. Command-Driven Special Procedures Special procedures are called at three points in code: the command interpreter (command-driven special procedures), the game heartbeat and the violence code (pulsed special procedures). + There is no information kept to know if the special procedure is -pulsed or command-driven, other than the behaviour of the special procedure +pulsed or command-driven, other than the behavior of the special procedure itself. When creating a special procedure you must keep in mind that it will be called in each of the three places and must therefore be prepared to react to all situations accordingly. + In the next two sub-sections we will present both types of special -procedures, and in the third sub-section we'll explain how to diferentiate +procedures, and in the third sub-section we'll explain how to differentiate the three types of call. + 4.2.1. Pulsed Special Procedures Every tick, the function heartbeat goes through the list of mobiles @@ -356,56 +1904,72 @@ and the list of objects, updating everyo as a special procedure associated to it, that special procedure is run with the parameters cmd and argument set to 0 and NULL respectively. The NULL used to be sent as an empty string (""), but this was changed in 3.0bpl19. + When there is a fight, the special procedure of a fighting mobile is called once per round with the same arguments. + 4.2.2. Command Driven Special Procedures Whenever a player issues a command, the command interpreter tries to identify it in the Master Command Table. If it succeeds, before running the command associated in the table, it checks for special procedures in the following order: + * room the player is in; * objects in the player's equipment (does not enter the containers); * objects in the player's inventory (does not enter the containers); * mobiles in the room; * objects in the room floor; + The first special procedure to succeed and return true finishes the interpreting of the command. + If no special procedure returned true or no special procedure was found, then the command interpreter runs the procedure specified in the Master Command Table. + 4.2.3. Preparing for all occurrences In order to make your special procedure react accordingly to all the different places where the special procedure can be called you need to distinguish those places. The way to do it is through the parameters. + Whenever cmd is 0, the special procedure has been called as a pulsed special procedure, otherwise it's a command-driven special procedure. + To detect if the procedure was called through the violence code, it is a pulsed special procedure and IS_FIGHTING(me) must be true. Of course, this only has meaning for a mobile special procedure. + 4.3. Relating Special Procedures to Objects, Mobiles, and Rooms The special procedures are assigned to the prototypes of the objects and mobiles, not to the instances themselves. There are functions provided to assign the special procedures: + * ASSIGNMOB(,) * ASSIGNOBJ(,) * ASSIGNROOM(,) + Stock CircleMUD also provides a place where to put all the special procedure assignments: the functions assign_mobiles, assign_objects and assign_rooms in spec_assign.c. + 4.4. The Special Procedure Function Header The function header of any special procedure is defined in the macro SPECIAL() and is as follows: + int ()(struct char_data *ch, void *me, int cmd, char *argument) + where is the name of the special procedure. + The parameters to the function are: + ch : Character that issued the command to command interpreter that triggered this special procedure. me : The mobile, object or room to which the special procedure is @@ -417,16 +1981,19 @@ where is the name of the special or an empty string if the special procedure is being called by the pulse code. + 4.5. The Special Procedure Return Value The return value of a special procedure is looked at only by the command interpreter and has meaning only for command-driven special procedures. Pulsed special procedures should always return false. + A command-driven special procedure can return two values: - false : The procedure did not process the command given and it + + FALSE : The procedure did not process the command given and it should be processed in the standard way by the command interpreter. - true : The procedure reacted to the command given and so, the + TRUE : The procedure reacted to the command given and so, the command interpreter should ignore it. Index: doc/sources/building.tex =================================================================== RCS file: /home/circledb/.cvs/circle/doc/sources/building.tex,v retrieving revision 1.1 retrieving revision 1.3 diff -u -p -r1.1 -r1.3 --- doc/sources/building.tex 15 Jan 2002 00:58:47 -0000 1.1 +++ doc/sources/building.tex 6 Sep 2002 12:06:56 -0000 1.3 @@ -1,6 +1,7 @@ \documentclass[11pt]{article} \usepackage{url} \usepackage{times} +\usepackage[T1]{fontenc} % should embed the font, just in case \usepackage{varioref} % Document typeset from the original document that was typeset by Jeremy Elson. % This document typeset by Alex Fletcher on Dec 4/2001 @@ -211,8 +212,8 @@ This number is critical; it is the ident 6 WATER_SWIM Water (swimmable). 7 WATER_NOSWIM Unswimmable water - boat required for passage. - 8 UNDERWATER Underwater. - 9 FLYING Wheee! + 8 FLYING Wheee! + 9 UNDERWATER Underwater. \end{verbatim} \item[Direction Fields and Extra Descriptions] This section defines the room's exits, if any, as well as any extra descriptions such as signs or strange objects that might be in the room. This section can be empty if the room has no exits and no extra descriptions. Otherwise, it can have any number of \texttt{D} (Direction Field) and \texttt{E} (Extra Description) sections, in any order. After all exits and extra descriptions have been listed, the end of the room is signaled with the letter \texttt{S}. The Direction Fields and Extra Descriptions are described in more detail in the following sections. \end{description} @@ -385,7 +386,7 @@ The format of mobiles varies depending o 262144 s NOTDEADYET Reserved for internal use. Do not set. \end{verbatim} -\item[Action Bitvector] A bitvector (see section~\vref{usingbitvectors}~`Using Bitvectors') with the following values: +\item[Affection Bitvector] A bitvector (see section~\vref{usingbitvectors}~`Using Bitvectors') with the following values: \begin{verbatim} 1 a BLIND Mob is blind. 2 b INVISIBLE Mob is invisible. Index: lib/world/obj/30.obj =================================================================== RCS file: /home/circledb/.cvs/circle/lib/world/obj/30.obj,v retrieving revision 1.4 retrieving revision 1.5 diff -u -p -r1.4 -r1.5 --- lib/world/obj/30.obj 1 Nov 2001 07:11:00 -0000 1.4 +++ lib/world/obj/30.obj 6 Sep 2002 12:13:18 -0000 1.5 @@ -24,8 +24,8 @@ A dark bottle of ale has been left here. 10 10 3 #3003 bottle firebreather~ -a firebreather~ -A firebreather has been left here.~ +a bottle~ +A bottle of firebreather has been left here.~ ~ 17 0 1 8 8 7 0 Index: src/Makefile.lcc =================================================================== RCS file: /home/circledb/.cvs/circle/src/Makefile.lcc,v retrieving revision 1.9 retrieving revision 1.10 diff -u -p -r1.9 -r1.10 --- src/Makefile.lcc 16 Jan 2002 03:11:13 -0000 1.9 +++ src/Makefile.lcc 30 Apr 2002 20:58:09 -0000 1.10 @@ -12,7 +12,7 @@ # thus less clutter on the screen during the make LCCDIR=c:\lccpub -DISTDIR=c:\circle30bpl21 +DISTDIR=c:\circle30bpl22 CFLAGS=-c -I$(LCCDIR)\include -DLCC_WIN32 CC=lcc OBJS=\ Index: src/act.comm.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.comm.c,v retrieving revision 1.23 retrieving revision 1.24 diff -u -p -r1.23 -r1.24 --- src/act.comm.c 28 Jan 2002 07:03:44 -0000 1.23 +++ src/act.comm.c 26 Sep 2002 00:38:48 -0000 1.24 @@ -457,7 +457,7 @@ ACMD(do_gen_comm) if (PRF_FLAGGED(ch, PRF_NOREPEAT)) send_to_char(ch, "%s", OK); else - send_to_char(ch, "%sYou %s, '%s'%s", COLOR_LEV(ch) >= C_CMP ? color_on : "", com_msgs[subcmd][1], argument, CCNRM(ch, C_CMP)); + send_to_char(ch, "%sYou %s, '%s'%s\r\n", COLOR_LEV(ch) >= C_CMP ? color_on : "", com_msgs[subcmd][1], argument, CCNRM(ch, C_CMP)); snprintf(buf1, sizeof(buf1), "$n %ss, '%s'", com_msgs[subcmd][1], argument); Index: src/act.informative.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.informative.c,v retrieving revision 1.55 retrieving revision 1.59 diff -u -p -r1.55 -r1.59 --- src/act.informative.c 9 Apr 2002 14:52:43 -0000 1.55 +++ src/act.informative.c 26 Sep 2002 00:41:37 -0000 1.59 @@ -290,7 +290,7 @@ void list_one_char(struct char_data *i, } if (IS_NPC(i)) - send_to_char(ch, "%c%s", UPPER(*i->player.short_descr), i->player.short_descr); + send_to_char(ch, "%c%s", UPPER(*i->player.short_descr), i->player.short_descr + 1); else send_to_char(ch, "%s %s", i->player.name, GET_TITLE(i)); @@ -421,13 +421,13 @@ void look_at_room(struct char_data *ch, sprintbit(ROOM_FLAGS(IN_ROOM(ch)), room_bits, buf, sizeof(buf)); send_to_char(ch, "[%5d] %s [ %s]", GET_ROOM_VNUM(IN_ROOM(ch)), world[IN_ROOM(ch)].name, buf); } else - send_to_char(ch, world[IN_ROOM(ch)].name); + send_to_char(ch, "%s", world[IN_ROOM(ch)].name); send_to_char(ch, "%s\r\n", CCNRM(ch, C_NRM)); if ((!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_BRIEF)) || ignore_brief || ROOM_FLAGGED(IN_ROOM(ch), ROOM_DEATH)) - send_to_char(ch, world[IN_ROOM(ch)].description); + send_to_char(ch, "%s", world[IN_ROOM(ch)].description); /* autoexits */ if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOEXIT)) @@ -890,9 +890,17 @@ ACMD(do_weather) }; if (OUTSIDE(ch)) + { send_to_char(ch, "The sky is %s and %s.\r\n", sky_look[weather_info.sky], weather_info.change >= 0 ? "you feel a warm wind from south" : "your foot tells you bad weather is due"); + if (GET_LEVEL(ch) >= LVL_GOD) + send_to_char(ch, "Pressure: %d (change: %d), Sky: %d (%s)\r\n", + weather_info.pressure, + weather_info.change, + weather_info.sky, + sky_look[weather_info.sky]); + } else send_to_char(ch, "You have no feeling about the weather at all.\r\n"); } @@ -1401,7 +1409,8 @@ ACMD(do_where) ACMD(do_levels) { char buf[MAX_STRING_LENGTH]; - size_t i, len = 0, nlen; + size_t i, len = 0; + int nlen; if (IS_NPC(ch)) { send_to_char(ch, "You ain't nothin' but a hound-dog.\r\n"); Index: src/act.item.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.item.c,v retrieving revision 1.51 retrieving revision 1.52 diff -u -p -r1.51 -r1.52 --- src/act.item.c 9 Apr 2002 14:12:15 -0000 1.51 +++ src/act.item.c 25 Sep 2002 23:44:45 -0000 1.52 @@ -66,6 +66,8 @@ void perform_put(struct char_data *ch, s { if (GET_OBJ_WEIGHT(cont) + GET_OBJ_WEIGHT(obj) > GET_OBJ_VAL(cont, 0)) act("$p won't fit in $P.", FALSE, ch, obj, cont, TO_CHAR); + else if (OBJ_FLAGGED(obj, ITEM_NODROP) && IN_ROOM(cont) != NOWHERE) + act("You can't get $p out of your hand.", FALSE, ch, obj, NULL, TO_CHAR); else { obj_from_char(obj); obj_to_obj(obj, cont); Index: src/act.movement.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.movement.c,v retrieving revision 1.35 retrieving revision 1.36 diff -u -p -r1.35 -r1.36 --- src/act.movement.c 15 Feb 2002 22:31:31 -0000 1.35 +++ src/act.movement.c 2 May 2002 02:58:39 -0000 1.36 @@ -393,22 +393,24 @@ void do_doorcmd(struct char_data *ch, st int ok_pick(struct char_data *ch, obj_vnum keynum, int pickproof, int scmd) { - int percent; + int percent, skill_lvl; + + if (scmd != SCMD_PICK) + return (1); percent = rand_number(1, 101); + skill_lvl = GET_SKILL(ch, SKILL_PICK_LOCK) + dex_app_skill[GET_DEX(ch)].p_locks; + + if (keynum == NOTHING) + send_to_char(ch, "Odd - you can't seem to find a keyhole.\r\n"); + else if (pickproof) + send_to_char(ch, "It resists your attempts to pick it.\r\n"); + else if (percent > skill_lvl) + send_to_char(ch, "You failed to pick the lock.\r\n"); + else + return (1); - if (scmd == SCMD_PICK) { - if (keynum == NOTHING) - send_to_char(ch, "Odd - you can't seem to find a keyhole.\r\n"); - else if (pickproof) - send_to_char(ch, "It resists your attempts to pick it.\r\n"); - else if (percent > GET_SKILL(ch, SKILL_PICK_LOCK)) - send_to_char(ch, "You failed to pick the lock.\r\n"); - else - return (1); - return (0); - } - return (1); + return (0); } Index: src/act.other.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.other.c,v retrieving revision 1.42 retrieving revision 1.45 diff -u -p -r1.42 -r1.45 --- src/act.other.c 1 Feb 2002 02:04:17 -0000 1.42 +++ src/act.other.c 26 Sep 2002 00:39:10 -0000 1.45 @@ -95,7 +95,7 @@ ACMD(do_quit) /* If someone is quitting in their house, let them load back here. */ if (!PLR_FLAGGED(ch, PLR_LOADROOM) && ROOM_FLAGGED(IN_ROOM(ch), ROOM_HOUSE)) - GET_LOADROOM(ch) = IN_ROOM(ch); + GET_LOADROOM(ch) = GET_ROOM_VNUM(IN_ROOM(ch)); extract_char(ch); /* Char is saved before extracting. */ } @@ -739,9 +739,16 @@ ACMD(do_display) skip_spaces(&argument); if (!*argument) { - send_to_char(ch, "Usage: prompt { { H | M | V } | all | none }\r\n"); + send_to_char(ch, "Usage: prompt { { H | M | V } | all | auto | none }\r\n"); return; } + + if (!str_cmp(argument, "auto")) { + TOGGLE_BIT(PRF_FLAGS(ch), PRF_DISPAUTO); + send_to_char(ch, "Auto prompt %sabled.\r\n", PRF_FLAGGED(ch, PRF_DISPAUTO) ? "en" : "dis"); + return; + } + if (!str_cmp(argument, "on") || !str_cmp(argument, "all")) SET_BIT(PRF_FLAGS(ch), PRF_DISPHP | PRF_DISPMANA | PRF_DISPMOVE); else if (!str_cmp(argument, "off") || !str_cmp(argument, "none")) @@ -761,7 +768,7 @@ ACMD(do_display) SET_BIT(PRF_FLAGS(ch), PRF_DISPMOVE); break; default: - send_to_char(ch, "Usage: prompt { { H | M | V } | all | none }\r\n"); + send_to_char(ch, "Usage: prompt { { H | M | V } | all | auto | none }\r\n"); return; } } @@ -839,7 +846,7 @@ ACMD(do_gen_write) ACMD(do_gen_tog) { - long result; + long /* bitvector_t */ result; const char *tog_messages[][2] = { {"You are now safe from summoning by other players.\r\n", Index: src/act.wizard.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/act.wizard.c,v retrieving revision 1.74 retrieving revision 1.76 diff -u -p -r1.74 -r1.76 --- src/act.wizard.c 9 Apr 2002 14:12:15 -0000 1.74 +++ src/act.wizard.c 26 Sep 2002 00:41:37 -0000 1.76 @@ -1701,12 +1701,12 @@ ACMD(do_wiznet) (!PRF_FLAGGED(d->character, PRF_NOWIZ)) && (!PLR_FLAGGED(d->character, PLR_WRITING | PLR_MAILING)) && (d != ch->desc || !(PRF_FLAGGED(d->character, PRF_NOREPEAT)))) { - send_to_char(d->character, CCCYN(d->character, C_NRM)); + send_to_char(d->character, "%s", CCCYN(d->character, C_NRM)); if (CAN_SEE(d->character, ch)) - send_to_char(d->character, buf1); + send_to_char(d->character, "%s", buf1); else - send_to_char(d->character, buf2); - send_to_char(d->character, CCNRM(d->character, C_NRM)); + send_to_char(d->character, "%s", buf2); + send_to_char(d->character, "%s", CCNRM(d->character, C_NRM)); } } @@ -1871,8 +1871,8 @@ size_t print_zone_to_buf(char *bufptr, s ACMD(do_show) { struct char_file_u vbuf; - int i, j, k, l, con; /* i, j, k to specifics? */ - size_t len, nlen; + int i, j, k, l, con, nlen; /* i, j, k to specifics? */ + size_t len; zone_rnum zrn; zone_vnum zvn; byte self = FALSE; Index: src/alias.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/alias.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -p -r1.8 -r1.9 --- src/alias.c 20 Mar 2002 22:30:27 -0000 1.8 +++ src/alias.c 18 May 2002 00:56:46 -0000 1.9 @@ -18,6 +18,7 @@ void write_aliases(struct char_data *ch); void read_aliases(struct char_data *ch); +void delete_aliases(const char *charname); void write_aliases(struct char_data *ch) { @@ -113,3 +114,15 @@ read_alias_error: prev->next = NULL; fclose(file); } + +void delete_aliases(const char *charname) +{ + char filename[PATH_MAX]; + + if (!get_filename(filename, sizeof(filename), ALIAS_FILE, charname)) + return; + + if (remove(filename) < 0 && errno != ENOENT) + log("SYSERR: deleting alias file %s: %s", filename, strerror(errno)); +} + Index: src/boards.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/boards.c,v retrieving revision 1.26 retrieving revision 1.28 diff -u -p -r1.26 -r1.28 --- src/boards.c 28 Jan 2002 17:24:37 -0000 1.26 +++ src/boards.c 25 Sep 2002 23:45:06 -0000 1.28 @@ -107,6 +107,12 @@ int find_board(struct char_data *ch) if (BOARD_RNUM(i) == GET_OBJ_RNUM(obj)) return (i); + if (GET_LEVEL(ch) >= LVL_IMMORT) + for (obj = ch->carrying; obj; obj = obj->next_content) + for (i = 0; i < NUM_OF_BOARDS; i++) + if (BOARD_RNUM(i) == GET_OBJ_RNUM(obj)) + return (i); + return (-1); } @@ -410,7 +416,11 @@ int Board_remove_msg(int board_type, str MSG_SLOTNUM(board_type, ind) = MSG_SLOTNUM(board_type, ind + 1); MSG_LEVEL(board_type, ind) = MSG_LEVEL(board_type, ind + 1); } + MSG_HEADING(board_type, num_of_msgs[board_type] - 1) = NULL; + MSG_SLOTNUM(board_type, num_of_msgs[board_type] - 1) = 0; + MSG_LEVEL(board_type, num_of_msgs[board_type] - 1) = 0; num_of_msgs[board_type]--; + send_to_char(ch, "Message removed.\r\n"); snprintf(buf, sizeof(buf), "$n just removed message %d.", msg); act(buf, FALSE, ch, 0, 0, TO_ROOM); Index: src/class.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/class.c,v retrieving revision 1.33 retrieving revision 1.34 diff -u -p -r1.33 -r1.34 --- src/class.c 9 Apr 2002 14:52:43 -0000 1.33 +++ src/class.c 30 Apr 2002 21:00:33 -0000 1.34 @@ -1810,7 +1810,7 @@ int level_exp(int chclass, int level) case 4: return 5000; case 5: return 10000; case 6: return 20000; - case 7: return 30000; + case 7: return 40000; case 8: return 70000; case 9: return 110000; case 10: return 160000; Index: src/comm.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/comm.c,v retrieving revision 1.114 retrieving revision 1.117 diff -u -p -r1.114 -r1.117 --- src/comm.c 9 Apr 2002 14:31:05 -0000 1.114 +++ src/comm.c 4 Oct 2002 07:14:38 -0000 1.117 @@ -108,6 +108,7 @@ struct timeval null_time; /* zero-valued byte reread_wizlist; /* signal: SIGUSR1 */ byte emergency_unban; /* signal: SIGUSR2 */ FILE *logfile = NULL; /* Where to send the log messages. */ +const char *text_overflow = "**OVERFLOW**\r\n"; /* functions in this file */ RETSIGTYPE reread_wizlists(int sig); @@ -150,7 +151,7 @@ sigfunc *my_signal(int signo, sigfunc *f /* extern fcnts */ void reboot_wizlists(void); void boot_world(void); -void affect_update(void); /* In spells.c */ +void affect_update(void); /* In magic.c */ void mobile_activity(void); void perform_violence(void); void show_string(struct descriptor_data *d, char *input); @@ -734,7 +735,7 @@ void game_loop(socket_t mother_desc) } GET_WAIT_STATE(d->character) = 1; } - d->has_prompt = 0; + d->has_prompt = FALSE; if (d->str) /* Writing boards, mail, etc. */ string_add(d, comm); @@ -744,7 +745,7 @@ void game_loop(socket_t mother_desc) nanny(d, comm); else { /* else: we're playing normally. */ if (aliased) /* To prevent recursive aliases. */ - d->has_prompt = 1; /* To get newline before next cmd output. */ + d->has_prompt = TRUE; /* To get newline before next cmd output. */ else if (perform_alias(d, comm, sizeof(comm))) /* Run it through aliasing system */ get_from_q(&d->input, comm, &aliased); command_interpreter(d->character, comm); /* Send it to interpreter */ @@ -755,19 +756,19 @@ void game_loop(socket_t mother_desc) for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (*(d->output) && FD_ISSET(d->descriptor, &output_set)) { - /* Output for this player is ready */ - if (process_output(d) < 0) - close_socket(d); - else - d->has_prompt = 1; + /* Output for this player is ready. */ + + process_output(d); + if (d->bufptr == 0) /* All output sent. */ + d->has_prompt = TRUE; } } /* Print prompts for other descriptors who had no other output */ for (d = descriptor_list; d; d = d->next) { - if (!d->has_prompt) { + if (!d->has_prompt && d->bufptr == 0) { write_to_descriptor(d->descriptor, make_prompt(d)); - d->has_prompt = 1; + d->has_prompt = TRUE; } } @@ -995,33 +996,41 @@ char *make_prompt(struct descriptor_data "\r\n[ Return to continue, (q)uit, (r)efresh, (b)ack, or page number (%d/%d) ]", d->showstr_page, d->showstr_count); } else if (STATE(d) == CON_PLAYING && !IS_NPC(d->character)) { - int count; + int count, idx; size_t len = 0; + struct prompt_value_data { + bitvector_t pref; + long cur_value; + long max_value; + const char *format; + } prompt_display[] = { + { 0, GET_INVIS_LEV(d->character), 0, "i%d " }, + { PRF_DISPHP, GET_HIT(d->character), GET_MAX_HIT(d->character), "%dH " }, + { PRF_DISPMANA, GET_MANA(d->character), GET_MAX_MANA(d->character), "%dM " }, + { PRF_DISPMOVE, GET_MOVE(d->character), GET_MAX_MOVE(d->character), "%dV " }, + }; *prompt = '\0'; - if (GET_INVIS_LEV(d->character) && len < sizeof(prompt)) { - count = snprintf(prompt + len, sizeof(prompt) - len, "i%d ", GET_INVIS_LEV(d->character)); - if (count >= 0) - len += count; - } - - if (PRF_FLAGGED(d->character, PRF_DISPHP) && len < sizeof(prompt)) { - count = snprintf(prompt + len, sizeof(prompt) - len, "%dH ", GET_HIT(d->character)); - if (count >= 0) - len += count; - } + for (idx = 0; idx < sizeof(prompt_display) / sizeof(struct prompt_value_data); idx++) { + if (prompt_display[idx].pref) { + if (PRF_FLAGGED(d->character, prompt_display[idx].pref)) + /* Always display. */ ; + else if (PRF_FLAGGED(d->character, PRF_DISPAUTO) && prompt_display[idx].cur_value * 3 < prompt_display[idx].max_value) + /* Under 33%, display. */ ; + else + continue; + } else if (!prompt_display[idx].cur_value) + continue; - if (PRF_FLAGGED(d->character, PRF_DISPMANA) && len < sizeof(prompt)) { - count = snprintf(prompt + len, sizeof(prompt) - len, "%dM ", GET_MANA(d->character)); + count = snprintf(prompt + len, sizeof(prompt) - len, prompt_display[idx].format, prompt_display[idx].cur_value); if (count >= 0) len += count; - } + else + len = sizeof(prompt); /* -1 return value: old versions of snprintf give when truncating. */ - if (PRF_FLAGGED(d->character, PRF_DISPMOVE) && len < sizeof(prompt)) { - count = snprintf(prompt + len, sizeof(prompt) - len, "%dV ", GET_MOVE(d->character)); - if (count >= 0) - len += count; + if (len >= sizeof(prompt)) + break; } if (len < sizeof(prompt)) @@ -1119,12 +1128,26 @@ size_t vwrite_to_output(struct descripto int size; /* if we're in the overflow state already, ignore this new output */ - if (t->bufptr < 0) + if (t->bufspace == 0) return (0); wantsize = size = vsnprintf(txt, sizeof(txt), format, args); - if (size < 0 || wantsize >= sizeof(txt)) + /* If exceeding the size of the buffer, truncate it for the overflow message */ + if (size < 0 || wantsize >= sizeof(txt)) { size = sizeof(txt) - 1; + strcpy(txt + size - strlen(text_overflow), text_overflow); /* strcpy: OK */ + } + + /* + * If the text is too big to fit into even a large buffer, truncate + * the new text to make it fit. (This will switch to the overflow + * state automatically because t->bufspace will end up 0.) + */ + if (size + t->bufptr + 1 > LARGE_BUFSIZE) { + size = LARGE_BUFSIZE - t->bufptr - 1; + txt[size] = '\0'; + buf_overflows++; + } /* if we have enough space, just write to buffer and that's it! */ if (t->bufspace >= size) { @@ -1134,16 +1157,6 @@ size_t vwrite_to_output(struct descripto return (t->bufspace); } - /* - * If the text is too big to fit into even a large buffer, chuck the - * the new text, and switch to the overflow state. - */ - /* FIXME: This should write a partial string up to what fits! */ - if (size + t->bufptr > LARGE_BUFSIZE - 1) { - t->bufptr = -1; - buf_overflows++; - return (0); - } buf_switches++; /* if the pool has a buffer in it, grab it */ @@ -1378,8 +1391,6 @@ int new_descriptor(socket_t s) /* * Send all of the output that we've accumulated for a player out to * the player's descriptor. - * FIXME - This will be rewritten before 3.1, this code is dumb. - * FIXME: string nightmare * * 32 byte GARBAGE_SPACE in MAX_SOCK_BUF used for: * 2 bytes: prepended \r\n @@ -1389,22 +1400,22 @@ int new_descriptor(socket_t s) */ int process_output(struct descriptor_data *t) { - char i[MAX_SOCK_BUF]; + char i[MAX_SOCK_BUF], *osb = i + 2; int result; /* we may need this \r\n for later -- see below */ strcpy(i, "\r\n"); /* strcpy: OK (for 'MAX_SOCK_BUF >= 3') */ /* now, append the 'real' output */ - strcpy(i + 2, t->output); /* strcpy: OK (t->output:LARGE_BUFSIZE < i:MAX_SOCK_BUF-2) */ + strcpy(osb, t->output); /* strcpy: OK (t->output:LARGE_BUFSIZE < osb:MAX_SOCK_BUF-2) */ /* if we're in the overflow state, notify the user */ - if (t->bufptr < 0) - strcat(i, "**OVERFLOW**\r\n"); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ + if (t->bufspace == 0) + strcat(osb, "**OVERFLOW**\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add the extra CRLF if the person isn't in compact mode */ if (STATE(t) == CON_PLAYING && t->character && !IS_NPC(t->character) && !PRF_FLAGGED(t->character, PRF_COMPACT)) - strcat(i, "\r\n"); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ + strcat(osb, "\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ /* add a prompt */ strcat(i, make_prompt(t)); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ @@ -1413,29 +1424,60 @@ int process_output(struct descriptor_dat * now, send the output. If this is an 'interruption', use the prepended * CRLF, otherwise send the straight output sans CRLF. */ - if (t->has_prompt) /* && !t->connected) */ + if (t->has_prompt) { + t->has_prompt = FALSE; result = write_to_descriptor(t->descriptor, i); - else - result = write_to_descriptor(t->descriptor, i + 2); + if (result >= 2) + result -= 2; + } else + result = write_to_descriptor(t->descriptor, osb); + + if (result < 0) { /* Oops, fatal error. Bye! */ + close_socket(t); + return (-1); + } else if (result == 0) /* Socket buffer full. Try later. */ + return (0); - /* handle snooping: prepend "% " and send to snooper */ + /* Handle snooping: prepend "% " and send to snooper. */ if (t->snoop_by) - write_to_output(t->snoop_by, "%% %s%%%%", t->output); + write_to_output(t->snoop_by, "%% %*s%%%%", result, t->output); - /* - * if we were using a large buffer, put the large buffer on the buffer pool - * and switch back to the small one - */ - if (t->large_outbuf) { - t->large_outbuf->next = bufpool; - bufpool = t->large_outbuf; - t->large_outbuf = NULL; - t->output = t->small_outbuf; - } - /* reset total bufspace back to that of a small buffer */ - t->bufspace = SMALL_BUFSIZE - 1; - t->bufptr = 0; - *(t->output) = '\0'; + /* The common case: all saved output was handed off to the kernel buffer. */ + if (result >= t->bufptr) { + /* + * if we were using a large buffer, put the large buffer on the buffer pool + * and switch back to the small one + */ + if (t->large_outbuf) { + t->large_outbuf->next = bufpool; + bufpool = t->large_outbuf; + t->large_outbuf = NULL; + t->output = t->small_outbuf; + } + /* reset total bufspace back to that of a small buffer */ + t->bufspace = SMALL_BUFSIZE - 1; + t->bufptr = 0; + *(t->output) = '\0'; + + /* + * If the overflow message or prompt were partially written, try to save + * them. There will be enough space for them if this is true. + */ + if (result < strlen(osb)) { + size_t savetextlen = strlen(osb + result); + + strcat(t->output, osb + result); + t->bufptr -= savetextlen; + t->bufspace += savetextlen; + } + + } else { + /* Not all data in buffer sent. result < output buffersize. */ + + strcpy(t->output, t->output + result); /* strcpy: OK (overlap) */ + t->bufptr -= result; + t->bufspace += result; + } return (result); } @@ -1546,13 +1588,13 @@ ssize_t perform_socket_write(socket_t de * encountered. * * Returns: - * 0 If all is well and good, - * -1 If an error was encountered, so that the player should be cut off + * >=0 If all is well and good. + * -1 If an error was encountered, so that the player should be cut off. */ int write_to_descriptor(socket_t desc, const char *txt) { ssize_t bytes_written; - size_t total = strlen(txt); + size_t total = strlen(txt), write_total = 0; while (total > 0) { bytes_written = perform_socket_write(desc, txt, total); @@ -1562,20 +1604,16 @@ int write_to_descriptor(socket_t desc, c perror("SYSERR: Write to socket"); return (-1); } else if (bytes_written == 0) { - /* - * Temporary failure -- socket buffer full. For now we'll just - * cut off the player, but eventually we'll stuff the unsent - * text into a buffer and retry the write later. JE 30 June 98. - */ - log("WARNING: write_to_descriptor: socket write would block, about to close"); - return (-1); + /* Temporary failure -- socket buffer full. */ + return (write_total); } else { txt += bytes_written; total -= bytes_written; + write_total += bytes_written; } } - return (0); + return (write_total); } Index: src/comm.h =================================================================== RCS file: /home/circledb/.cvs/circle/src/comm.h,v retrieving revision 1.13 retrieving revision 1.14 diff -u -p -r1.13 -r1.14 --- src/comm.h 21 Feb 2002 01:28:00 -0000 1.13 +++ src/comm.h 21 Jun 2002 23:24:28 -0000 1.14 @@ -15,7 +15,6 @@ size_t send_to_char(struct char_data *ch void send_to_all(const char *messg, ...) __attribute__ ((format (printf, 1, 2))); void send_to_room(room_rnum room, const char *messg, ...) __attribute__ ((format (printf, 2, 3))); void send_to_outdoor(const char *messg, ...) __attribute__ ((format (printf, 1, 2))); -void perform_to_all(const char *messg, struct char_data *ch); void close_socket(struct descriptor_data *d); void perform_act(const char *orig, struct char_data *ch, Index: src/conf.h.win =================================================================== RCS file: /home/circledb/.cvs/circle/src/conf.h.win,v retrieving revision 1.8 retrieving revision 1.9 diff -u -p -r1.8 -r1.9 --- src/conf.h.win 2 Aug 1999 00:28:53 -0000 1.8 +++ src/conf.h.win 18 May 2002 00:58:41 -0000 1.9 @@ -6,10 +6,16 @@ #define CIRCLE_WINDOWS /* Define to empty if the keyword does not work. */ -/* #undef const */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT /* Define if you have that is POSIX.1 compatible. */ -/* #undef HAVE_SYS_WAIT_H */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 /* Define to `int' if doesn't define. */ #define pid_t int @@ -18,43 +24,85 @@ #define RETSIGTYPE void /* Define to `unsigned' if doesn't define. */ -/* #undef size_t */ - -/* Define to `int' if doesn't define. */ -#define socklen_t int +#undef size_t /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you can safely include both and . */ -/* #undef TIME_WITH_SYS_TIME */ +#undef TIME_WITH_SYS_TIME -/* Define if we're compiling CircleMUD under any type of UNIX system */ -/* #undef CIRCLE_UNIX */ +/* Define if we're compiling CircleMUD under any type of UNIX system. */ +#undef CIRCLE_UNIX + +/* Define if the system is capable of using crypt() to encrypt. */ +#undef CIRCLE_CRYPT + +/* Define if we don't have proper support for the system's crypt(). */ +#undef HAVE_UNSAFE_CRYPT + +/* Define is the system has struct in_addr. */ +#define HAVE_STRUCT_IN_ADDR 1 -/* Define if the system is capable of using crypt() to encrypt */ -/* #undef CIRCLE_CRYPT */ +/* Define to `int' if doesn't define. */ +#define socklen_t int /* Define to `int' if doesn't define. */ #define ssize_t int +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + /* Define if you have the inet_addr function. */ #define HAVE_INET_ADDR 1 /* Define if you have the inet_aton function. */ -/* #undef HAVE_INET_ATON */ +#undef HAVE_INET_ATON + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the strcasecmp function. */ +#undef HAVE_STRCASECMP + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the stricmp function. */ +#define HAVE_STRICMP 1 + +/* Define if you have the strlcpy function. */ +#undef HAVE_STRLCPY + +/* Define if you have the strncasecmp function. */ +#undef HAVE_STRNCASECMP + +/* Define if you have the strnicmp function. */ +#define HAVE_STRNICMP 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 /* Define if you have the header file. */ -/* #undef HAVE_ARPA_INET_H */ +#undef HAVE_ARPA_INET_H /* Define if you have the header file. */ -/* #undef HAVE_ARPA_TELNET_H */ +#undef HAVE_ARPA_TELNET_H /* Define if you have the header file. */ #define HAVE_ASSERT_H 1 /* Define if you have the header file. */ -/* #undef HAVE_CRYPT_H */ +#undef HAVE_CRYPT_H /* Define if you have the header file. */ #define HAVE_ERRNO_H 1 @@ -65,204 +113,224 @@ /* Define if you have the header file. */ #define HAVE_LIMITS_H 1 +/* Define if you have the header file. */ +#undef HAVE_MCHECK_H + /* Define if you have the header file. */ -/* #undef HAVE_MEMORY_H */ +#undef HAVE_MEMORY_H /* Define if you have the header file. */ -/* #undef HAVE_NET_ERRNO_H */ +#undef HAVE_NET_ERRNO_H /* Define if you have the header file. */ -/* #undef HAVE_NETDB_H */ +#undef HAVE_NETDB_H /* Define if you have the header file. */ -/* #undef HAVE_NETINET_IN_H */ +#undef HAVE_NETINET_IN_H /* Define if you have the header file. */ -/* #undef HAVE_SIGNAL_H */ +#undef HAVE_SIGNAL_H /* Define if you have the header file. */ #define HAVE_STRING_H 1 /* Define if you have the header file. */ -/* #undef HAVE_STRINGS_H */ +#undef HAVE_STRINGS_H /* Define if you have the header file. */ -/* #undef HAVE_SYS_FCNTL_H */ +#undef HAVE_SYS_FCNTL_H /* Define if you have the header file. */ -/* #undef HAVE_SYS_RESOURCE_H */ +#undef HAVE_SYS_RESOURCE_H /* Define if you have the header file. */ -/* #undef HAVE_SYS_SELECT_H */ +#undef HAVE_SYS_SELECT_H /* Define if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ +#undef HAVE_SYS_SOCKET_H /* Define if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define if you have the header file. */ -/* #undef HAVE_SYS_TIME_H */ +#undef HAVE_SYS_TIME_H /* Define if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define if you have the header file. */ -/* #undef HAVE_SYS_UIO_H */ +#undef HAVE_SYS_UIO_H /* Define if you have the header file. */ -/* #undef HAVE_UNISTD_H */ +#undef HAVE_UNISTD_H /* Define if you have the malloc library (-lmalloc). */ -/* #undef HAVE_LIBMALLOC */ +#undef HAVE_LIBMALLOC -/* Define if your compiler does not prototype accept(). */ +/* Check for a prototype to accept. */ #undef NEED_ACCEPT_PROTO -/* Define if your compiler does not prototype atoi(). */ +/* Check for a prototype to atoi. */ #undef NEED_ATOI_PROTO -/* Define if your compiler does not prototype atol(). */ +/* Check for a prototype to atol. */ #undef NEED_ATOL_PROTO -/* Define if your compiler does not prototype bind(). */ +/* Check for a prototype to bind. */ #undef NEED_BIND_PROTO -/* Define if your compiler does not prototype bzero(). */ +/* Check for a prototype to bzero. */ #undef NEED_BZERO_PROTO -/* Define if your compiler does not prototype chdir(). */ +/* Check for a prototype to chdir. */ #undef NEED_CHDIR_PROTO -/* Define if your compiler does not prototype close(). */ +/* Check for a prototype to close. */ #undef NEED_CLOSE_PROTO -/* Define if your compiler does not prototype crypt(). */ +/* Check for a prototype to crypt. */ #undef NEED_CRYPT_PROTO -/* Define if your compiler does not prototype fclose(). */ +/* Check for a prototype to fclose. */ #undef NEED_FCLOSE_PROTO -/* Define if your compiler does not prototype fcntl(). */ +/* Check for a prototype to fcntl. */ #undef NEED_FCNTL_PROTO -/* Define if your compiler does not prototype fflush(). */ +/* Check for a prototype to fflush. */ #undef NEED_FFLUSH_PROTO -/* Define if your compiler does not prototype fprintf(). */ +/* Check for a prototype to fprintf. */ #undef NEED_FPRINTF_PROTO -/* Define if your compiler does not prototype fputc(). */ +/* Check for a prototype to fputc. */ #undef NEED_FPUTC_PROTO -/* Define if your compiler does not prototype fputs(). */ +/* Check for a prototype to fputs. */ #undef NEED_FPUTS_PROTO -/* Define if your compiler does not prototype fread(). */ +/* Check for a prototype to fread. */ #undef NEED_FREAD_PROTO -/* Define if your compiler does not prototype fscanf(). */ +/* Check for a prototype to fscanf. */ #undef NEED_FSCANF_PROTO -/* Define if your compiler does not prototype fseek(). */ +/* Check for a prototype to fseek. */ #undef NEED_FSEEK_PROTO -/* Define if your compiler does not prototype fwrite(). */ +/* Check for a prototype to fwrite. */ #undef NEED_FWRITE_PROTO -/* Define if your compiler does not prototype getpeername(). */ +/* Check for a prototype to getpeername. */ #undef NEED_GETPEERNAME_PROTO -/* Define if your compiler does not prototype getpid(). */ +/* Check for a prototype to getpid. */ #undef NEED_GETPID_PROTO -/* Define if your compiler does not prototype getrlimit(). */ +/* Check for a prototype to getrlimit. */ #undef NEED_GETRLIMIT_PROTO -/* Define if your compiler does not prototype getsockname(). */ +/* Check for a prototype to getsockname. */ #undef NEED_GETSOCKNAME_PROTO -/* Define if your compiler does not prototype gettimeofday(). */ +/* Check for a prototype to gettimeofday. */ #undef NEED_GETTIMEOFDAY_PROTO -/* Define if your compiler does not prototype htonl(). */ +/* Check for a prototype to htonl. */ #undef NEED_HTONL_PROTO -/* Define if your compiler does not prototype htons(). */ +/* Check for a prototype to htons. */ #undef NEED_HTONS_PROTO -/* Define if your compiler does not prototype inet_addr(). */ +/* Check for a prototype to inet_addr. */ #undef NEED_INET_ADDR_PROTO -/* Define if your compiler does not prototype inet_aton(). */ +/* Check for a prototype to inet_aton. */ #undef NEED_INET_ATON_PROTO -/* Define if your compiler does not prototype inet_ntoa(). */ +/* Check for a prototype to inet_ntoa. */ #undef NEED_INET_NTOA_PROTO -/* Define if your compiler does not prototype listen(). */ +/* Check for a prototype to listen. */ #undef NEED_LISTEN_PROTO -/* Define if your compiler does not prototype ntohl(). */ +/* Check for a prototype to ntohl. */ #undef NEED_NTOHL_PROTO -/* Define if your compiler does not prototype perror(). */ +/* Check for a prototype to perror. */ #undef NEED_PERROR_PROTO -/* Define if your compiler does not prototype printf(). */ +/* Check for a prototype to printf. */ #undef NEED_PRINTF_PROTO -/* Define if your compiler does not prototype qsort(). */ +/* Check for a prototype to qsort. */ #undef NEED_QSORT_PROTO -/* Define if your compiler does not prototype read(). */ +/* Check for a prototype to read. */ #undef NEED_READ_PROTO -/* Define if your compiler does not prototype rewind(). */ +/* Check for a prototype to remove. */ +#undef NEED_REMOVE_PROTO + +/* Check for a prototype to rewind. */ #undef NEED_REWIND_PROTO -/* Define if your compiler does not prototype select(). */ +/* Check for a prototype to select. */ #undef NEED_SELECT_PROTO -/* Define if your compiler does not prototype setitimer(). */ +/* Check for a prototype to setitimer. */ #undef NEED_SETITIMER_PROTO -/* Define if your compiler does not prototype setrlimit(). */ +/* Check for a prototype to setrlimit. */ #undef NEED_SETRLIMIT_PROTO -/* Define if your compiler does not prototype setsockopt(). */ +/* Check for a prototype to setsockopt. */ #undef NEED_SETSOCKOPT_PROTO -/* Define if your compiler does not prototype socket(). */ +/* Check for a prototype to snprintf. */ +#undef NEED_SNPRINTF_PROTO + +/* Check for a prototype to socket. */ #undef NEED_SOCKET_PROTO -/* Define if your compiler does not prototype sprintf(). */ +/* Check for a prototype to sprintf. */ #undef NEED_SPRINTF_PROTO -/* Define if your compiler does not prototype sscanf(). */ +/* Check for a prototype to sscanf. */ #undef NEED_SSCANF_PROTO -/* Define if your compiler does not prototype system(). */ -#undef NEED_SYSTEM_PROTO +/* Check for a prototype to strcasecmp. */ +#undef NEED_STRCASECMP_PROTO -/* Define if your compiler does not prototype time(). */ -#undef NEED_TIME_PROTO +/* Check for a prototype to strdup. */ +#undef NEED_STRDUP_PROTO -/* Define if your compiler does not prototype unlink(). */ -#undef NEED_UNLINK_PROTO +/* Check for a prototype to strerror. */ +#undef NEED_STRERROR_PROTO -/* Define if your compiler does not prototype write(). */ -#undef NEED_WRITE_PROTO +/* Check for a prototype to stricmp. */ +#undef NEED_STRICMP_PROTO -/* Define if your compiler does not prototype remove(). */ -/* #undef NEED_REMOVE_PROTO */ +/* Check for a prototype to strlcpy. */ +#define NEED_STRLCPY_PROTO 1 -/* Define if your compiler does not prototype strerror(). */ -/* #undef NEED_STRERROR_PROTO */ +/* Check for a prototype to strncasecmp. */ +#undef NEED_STRNCASECMP_PROTO -/* Define if you have 'struct in_addr' */ -#define HAVE_STRUCT_IN_ADDR 1 +/* Check for a prototype to strnicmp. */ +#undef NEED_STRNICMP_PROTO -/* Define if your crypt isn't safe with only 10 characters. */ -#undef HAVE_UNSAFE_CRYPT +/* Check for a prototype to system. */ +#undef NEED_SYSTEM_PROTO + +/* Check for a prototype to time. */ +#undef NEED_TIME_PROTO + +/* Check for a prototype to unlink. */ +#undef NEED_UNLINK_PROTO +/* Check for a prototype to vsnprintf. */ +#undef NEED_VSNPRINTF_PROTO + +/* Check for a prototype to write. */ +#undef NEED_WRITE_PROTO Index: src/constants.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/constants.c,v retrieving revision 1.24 retrieving revision 1.25 diff -u -p -r1.24 -r1.25 --- src/constants.c 14 Sep 2001 10:08:57 -0000 1.24 +++ src/constants.c 25 Apr 2002 06:04:53 -0000 1.25 @@ -15,7 +15,7 @@ #include "interpreter.h" /* alias_data */ cpp_extern const char *circlemud_version = - "CircleMUD, version 3.00 beta patchlevel 19"; + "CircleMUD, version 3.00 beta patchlevel 22"; /* strings corresponding to ordinals/bitvectors in structs.h ***********/ Index: src/db.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/db.c,v retrieving revision 1.100 retrieving revision 1.101 diff -u -p -r1.100 -r1.101 --- src/db.c 9 Apr 2002 14:12:15 -0000 1.100 +++ src/db.c 25 Apr 2002 06:05:28 -0000 1.101 @@ -2422,7 +2422,7 @@ int create_entry(char *name) /* read and allocate space for a '~'-terminated string from a given file */ char *fread_string(FILE *fl, const char *error) { - char buf[MAX_STRING_LENGTH], tmp[512]; + char buf[MAX_STRING_LENGTH], tmp[513]; char *point; int done = 0, length = 0, templength; Index: src/handler.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/handler.c,v retrieving revision 1.48 retrieving revision 1.49 diff -u -p -r1.48 -r1.49 --- src/handler.c 9 Apr 2002 12:34:36 -0000 1.48 +++ src/handler.c 2 May 2002 20:44:43 -0000 1.49 @@ -238,7 +238,7 @@ void affect_total(struct char_data *ch) /* Make certain values are between 0..25, not < 0 and not > 25! */ - i = (IS_NPC(ch) ? 25 : 18); + i = (IS_NPC(ch) || GET_LEVEL(ch) >= LVL_GRGOD) ? 25 : 18; GET_DEX(ch) = MAX(0, MIN(GET_DEX(ch), i)); GET_INT(ch) = MAX(0, MIN(GET_INT(ch), i)); Index: src/handler.h =================================================================== RCS file: /home/circledb/.cvs/circle/src/handler.h,v retrieving revision 1.13 retrieving revision 1.14 diff -u -p -r1.13 -r1.14 --- src/handler.h 18 Oct 2001 02:18:06 -0000 1.13 +++ src/handler.h 21 Jun 2002 23:23:51 -0000 1.14 @@ -35,11 +35,6 @@ void equip_char(struct char_data *ch, st struct obj_data *unequip_char(struct char_data *ch, int pos); int invalid_align(struct char_data *ch, struct obj_data *obj); -struct obj_data *get_obj_in_list(char *name, struct obj_data *list); -struct obj_data *get_obj_in_list_num(int num, struct obj_data *list); -struct obj_data *get_obj(char *name); -struct obj_data *get_obj_num(obj_rnum nr); - void obj_to_room(struct obj_data *object, room_rnum room); void obj_from_room(struct obj_data *object); void obj_to_obj(struct obj_data *obj, struct obj_data *obj_to); @@ -65,6 +60,8 @@ struct char_data *get_char_vis(struct ch struct char_data *get_char_room_vis(struct char_data *ch, char *name, int *number); struct char_data *get_char_world_vis(struct char_data *ch, char *name, int *number); +struct obj_data *get_obj_in_list_num(int num, struct obj_data *list); +struct obj_data *get_obj_num(obj_rnum nr); struct obj_data *get_obj_in_list_vis(struct char_data *ch, char *name, int *number, struct obj_data *list); struct obj_data *get_obj_vis(struct char_data *ch, char *name, int *num); struct obj_data *get_obj_in_equip_vis(struct char_data *ch, char *arg, int *number, struct obj_data *equipment[]); @@ -95,7 +92,6 @@ int generic_find(char *arg, bitvector_t /* prototypes from crash save system */ -int Crash_get_filename(char *orig_name, char *filename); int Crash_delete_file(char *name); int Crash_delete_crashfile(struct char_data *ch); int Crash_clean_file(char *name); Index: src/house.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/house.c,v retrieving revision 1.28 retrieving revision 1.29 diff -u -p -r1.28 -r1.29 --- src/house.c 1 Feb 2002 02:19:39 -0000 1.28 +++ src/house.c 2 May 2002 03:13:33 -0000 1.29 @@ -588,7 +588,7 @@ int House_can_enter(struct char_data *ch void House_list_guests(struct char_data *ch, int i, int quiet) { - int j; + int j, num_printed; char *temp; if (house_control[i].num_of_guests == 0) { @@ -599,12 +599,17 @@ void House_list_guests(struct char_data send_to_char(ch, " Guests: "); - for (j = 0; j < house_control[i].num_of_guests; j++) { + for (num_printed = j = 0; j < house_control[i].num_of_guests; j++) { /* Avoid . -gg 6/21/98 */ if ((temp = get_name_by_id(house_control[i].guests[j])) == NULL) continue; + + num_printed++; send_to_char(ch, "%c%s ", UPPER(*temp), temp + 1); } + + if (num_printed == 0) + send_to_char(ch, "all dead"); send_to_char(ch, "\r\n"); } Index: src/interpreter.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/interpreter.c,v retrieving revision 1.40 retrieving revision 1.43 diff -u -p -r1.40 -r1.43 --- src/interpreter.c 9 Apr 2002 14:12:15 -0000 1.40 +++ src/interpreter.c 4 Oct 2002 04:24:47 -0000 1.43 @@ -50,6 +50,7 @@ int special(struct char_data *ch, int cm int isbanned(char *hostname); int Valid_Name(char *newname); void read_aliases(struct char_data *ch); +void delete_aliases(const char *charname); /* local functions */ int perform_dupe_check(struct descriptor_data *d); @@ -1145,10 +1146,7 @@ int _parse_name(char *arg, char *name) #define USURP 2 #define UNSWITCH 3 -/* - * XXX: Make immortals 'return' instead of being disconnected when switched - * into person returns. This function seems a bit over-extended too. - */ +/* This function seems a bit over-extended. */ int perform_dupe_check(struct descriptor_data *d) { struct descriptor_data *k, *next_k; @@ -1168,7 +1166,9 @@ int perform_dupe_check(struct descriptor if (k == d) continue; - if (k->original && (GET_IDNUM(k->original) == id)) { /* switched char */ + if (k->original && (GET_IDNUM(k->original) == id)) { + /* Original descriptor was switched, booting it and restoring normal body control. */ + write_to_output(d, "\r\nMultiple login detected -- disconnecting.\r\n"); STATE(k) = CON_CLOSE; if (!target) { @@ -1179,7 +1179,13 @@ int perform_dupe_check(struct descriptor k->character->desc = NULL; k->character = NULL; k->original = NULL; - } else if (k->character && (GET_IDNUM(k->character) == id)) { + } else if (k->character && GET_IDNUM(k->character) == id && k->original) { + /* Character taking over their own body, while an immortal was switched to it. */ + + do_return(k->character, NULL, 0, 0); + } else if (k->character && GET_IDNUM(k->character) == id) { + /* Character taking over their own body. */ + if (!target && STATE(k) == CON_PLAYING) { write_to_output(k, "\r\nThis body has been usurped!\r\n"); target = k->character; @@ -1232,7 +1238,7 @@ int perform_dupe_check(struct descriptor extract_char(ch); } - /* no target for swicthing into was found - allow login to continue */ + /* no target for switching into was found - allow login to continue */ if (!target) return (0); @@ -1366,8 +1372,7 @@ void nanny(struct descriptor_data *d, ch STATE(d) = CON_NEWPASSWD; } else if (*arg == 'n' || *arg == 'N') { write_to_output(d, "Okay, what IS it, then? "); - free(d->character->player.name); - d->character->player.name = NULL; + free_char(d->character); STATE(d) = CON_GET_NAME; } else write_to_output(d, "Please type Yes or No: "); @@ -1666,6 +1671,7 @@ void nanny(struct descriptor_data *d, ch SET_BIT(PLR_FLAGS(d->character), PLR_DELETED); save_char(d->character); Crash_delete_file(GET_NAME(d->character)); + delete_aliases(GET_NAME(d->character)); write_to_output(d, "Character '%s' deleted!\r\n" "Goodbye.\r\n", GET_NAME(d->character)); mudlog(NRM, LVL_GOD, TRUE, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GET_LEVEL(d->character)); Index: src/magic.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/magic.c,v retrieving revision 1.35 retrieving revision 1.36 diff -u -p -r1.35 -r1.36 --- src/magic.c 1 Feb 2002 02:04:17 -0000 1.35 +++ src/magic.c 4 Oct 2002 04:02:14 -0000 1.36 @@ -823,15 +823,21 @@ void mag_points(int level, struct char_d void mag_unaffects(int level, struct char_data *ch, struct char_data *victim, int spellnum, int type) { - int spell = 0; + int spell = 0, msg_not_affected = TRUE; const char *to_vict = NULL, *to_room = NULL; if (victim == NULL) return; switch (spellnum) { - case SPELL_CURE_BLIND: case SPELL_HEAL: + /* + * Heal also restores health, so don't give the "no effect" message + * if the target isn't afflicted by the 'blindness' spell. + */ + msg_not_affected = FALSE; + /* fall-through */ + case SPELL_CURE_BLIND: spell = SPELL_BLINDNESS; to_vict = "Your vision returns!"; to_room = "There's a momentary gleam in $n's eyes."; @@ -851,7 +857,7 @@ void mag_unaffects(int level, struct cha } if (!affected_by_spell(victim, spell)) { - if (spellnum != SPELL_HEAL) /* 'cure blindness' message. */ + if (msg_not_affected) send_to_char(ch, "%s", NOEFFECT); return; } Index: src/objsave.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/objsave.c,v retrieving revision 1.43 retrieving revision 1.44 diff -u -p -r1.43 -r1.44 --- src/objsave.c 20 Mar 2002 22:30:27 -0000 1.43 +++ src/objsave.c 2 May 2002 03:12:41 -0000 1.44 @@ -1066,28 +1066,33 @@ int gen_receptionist(struct char_data *c const char *action_table[] = { "smile", "dance", "sigh", "blush", "burp", "cough", "fart", "twiddle", "yawn" }; - if (!ch->desc || IS_NPC(ch)) - return (FALSE); - if (!cmd && !rand_number(0, 5)) { do_action(recep, NULL, find_command(action_table[rand_number(0, 8)]), 0); return (FALSE); } + + if (!ch->desc || IS_NPC(ch)) + return (FALSE); + if (!CMD_IS("offer") && !CMD_IS("rent")) return (FALSE); + if (!AWAKE(recep)) { send_to_char(ch, "%s is unable to talk to you...\r\n", HSSH(recep)); return (TRUE); } + if (!CAN_SEE(recep, ch)) { act("$n says, 'I don't deal with people I can't see!'", FALSE, recep, 0, 0, TO_ROOM); return (TRUE); } + if (free_rent) { act("$n tells you, 'Rent is free here. Just quit, and your objects will be saved!'", FALSE, recep, 0, ch, TO_VICT); return (1); } + if (CMD_IS("rent")) { char buf[128]; Index: src/shop.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/shop.c,v retrieving revision 1.50 retrieving revision 1.56 diff -u -p -r1.50 -r1.56 --- src/shop.c 9 Apr 2002 14:12:15 -0000 1.50 +++ src/shop.c 4 Oct 2002 04:38:04 -0000 1.56 @@ -914,7 +914,7 @@ void shopping_list(char *arg, struct cha lindex++; if (!last_obj) /* we actually have nothing in our list for sale, period */ send_to_char(ch, "Currently, there is nothing for sale.\r\n"); - else if (!found) /* nothing the char was looking for was found */ + else if (*name && !found) /* nothing the char was looking for was found */ send_to_char(ch, "Presently, none of those are for sale.\r\n"); else { if (!*name || isname(name, last_obj->name)) /* show last obj */ @@ -1246,11 +1246,15 @@ void assign_the_shopkeepers(void) cmd_emote = find_command("emote"); cmd_slap = find_command("slap"); cmd_puke = find_command("puke"); + for (cindex = 0; cindex <= top_shop; cindex++) { if (SHOP_KEEPER(cindex) == NOBODY) continue; - if (mob_index[SHOP_KEEPER(cindex)].func) + + /* Having SHOP_FUNC() as 'shop_keeper' will cause infinite recursion. */ + if (mob_index[SHOP_KEEPER(cindex)].func && mob_index[SHOP_KEEPER(cindex)].func != shop_keeper) SHOP_FUNC(cindex) = mob_index[SHOP_KEEPER(cindex)].func; + mob_index[SHOP_KEEPER(cindex)].func = shop_keeper; } } @@ -1258,31 +1262,32 @@ void assign_the_shopkeepers(void) char *customer_string(int shop_nr, int detailed) { - int sindex, cnt = 1, nlen; + int sindex = 0, flag = 1, nlen; size_t len = 0; static char buf[256]; - for (sindex = 0; *trade_letters[sindex] != '\n' && len + 1 < sizeof(buf); sindex++, cnt <<= 1) - if (SHOP_TRADE_WITH(shop_nr) & cnt) { - if (!detailed) { - strlcpy(buf + len, "_", sizeof(buf) - len); - len++; - if (len >= sizeof(buf)) - break; - } - } else { - if (detailed) { - nlen = snprintf(buf + len, sizeof(buf) - len, "%s%s", len > 0 ? ", " : "", trade_letters[sindex]); + while (*trade_letters[sindex] != '\n' && len + 1 < sizeof(buf)) { + if (detailed) { + if (!IS_SET(flag, SHOP_TRADE_WITH(shop_nr))) { + nlen = snprintf(buf + len, sizeof(buf) - len, ", %s", trade_letters[sindex]); + if (len + nlen >= sizeof(buf) || nlen < 0) break; + len += nlen; - } else if (len + 2 <= sizeof(buf)) { - buf[len++] = *trade_letters[sindex]; - buf[len++] = '\0'; - } else + } + } else { + buf[len++] = (IS_SET(flag, SHOP_TRADE_WITH(shop_nr)) ? '_' : *trade_letters[sindex]); + buf[len] = '\0'; + + if (len >= sizeof(buf)) break; } + sindex++; + flag <<= 1; + } + buf[sizeof(buf) - 1] = '\0'; return (buf); } @@ -1317,9 +1322,11 @@ void list_all_shops(struct char_data *ch else sprintf(buf1, "%6d", mob_index[SHOP_KEEPER(shop_nr)].vnum); /* sprintf: OK (for 'buf1 >= 11', 32-bit int) */ - snprintf(buf + len, sizeof(buf) - len, "%3d %6d %6d %s %3.2f %3.2f %s\r\n", - shop_nr + 1, SHOP_NUM(shop_nr), SHOP_ROOM(shop_nr, 0), buf1, - SHOP_SELLPROFIT(shop_nr), SHOP_BUYPROFIT(shop_nr), customer_string(shop_nr, FALSE)); + len += snprintf(buf + len, sizeof(buf) - len, + "%3d %6d %6d %s %3.2f %3.2f %s\r\n", + shop_nr + 1, SHOP_NUM(shop_nr), SHOP_ROOM(shop_nr, 0), buf1, + SHOP_SELLPROFIT(shop_nr), SHOP_BUYPROFIT(shop_nr), + customer_string(shop_nr, FALSE)); } page_string(ch->desc, buf, TRUE); @@ -1363,11 +1370,10 @@ void list_detailed_shop(struct char_data column += linelen; } if (!sindex) - send_to_char(ch, "Rooms: None!\r\n"); - + send_to_char(ch, "Rooms: None!"); - send_to_char(ch, "Shopkeeper: "); - if (SHOP_KEEPER(shop_nr) >= 0) { + send_to_char(ch, "\r\nShopkeeper: "); + if (SHOP_KEEPER(shop_nr) != NOBODY) { send_to_char(ch, "%s (#%d), Special Function: %s\r\n", GET_NAME(&mob_proto[SHOP_KEEPER(shop_nr)]), mob_index[SHOP_KEEPER(shop_nr)].vnum, @@ -1409,10 +1415,9 @@ void list_detailed_shop(struct char_data column += linelen; } if (!sindex) - send_to_char(ch, "Produces: Nothing!\r\n"); + send_to_char(ch, "Produces: Nothing!"); - - send_to_char(ch, "Buys: "); + send_to_char(ch, "\r\nBuys: "); column = 12; /* ^^^ strlen ^^^ */ for (sindex = 0; SHOP_BUYTYPE(shop_nr, sindex) != NOTHING; sindex++) { char buf1[128]; @@ -1440,10 +1445,9 @@ void list_detailed_shop(struct char_data column += linelen; } if (!sindex) - send_to_char(ch, "Buys: Nothing!\r\n"); - + send_to_char(ch, "Buys: Nothing!"); - send_to_char(ch, "Buy at: [%4.2f], Sell at: [%4.2f], Open: [%d-%d, %d-%d]\r\n", + send_to_char(ch, "\r\nBuy at: [%4.2f], Sell at: [%4.2f], Open: [%d-%d, %d-%d]\r\n", SHOP_SELLPROFIT(shop_nr), SHOP_BUYPROFIT(shop_nr), SHOP_OPEN1(shop_nr), SHOP_CLOSE1(shop_nr), SHOP_OPEN2(shop_nr), SHOP_CLOSE2(shop_nr)); Index: src/spec_procs.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/spec_procs.c,v retrieving revision 1.40 retrieving revision 1.41 diff -u -p -r1.40 -r1.41 --- src/spec_procs.c 9 Apr 2002 14:33:12 -0000 1.40 +++ src/spec_procs.c 26 Sep 2002 00:41:37 -0000 1.41 @@ -122,8 +122,8 @@ extern int prac_params[4][NUM_CLASSES]; void list_skills(struct char_data *ch) { const char *overflow = "\r\n**OVERFLOW**\r\n"; - int i, sortpos; - size_t len = 0, nlen; + int i, sortpos, nlen; + size_t len = 0; char buf2[MAX_STRING_LENGTH]; if (!GET_PRACTICES(ch)) { Index: src/structs.h =================================================================== RCS file: /home/circledb/.cvs/circle/src/structs.h,v retrieving revision 1.51 retrieving revision 1.53 diff -u -p -r1.51 -r1.53 --- src/structs.h 14 Apr 2002 18:24:24 -0000 1.51 +++ src/structs.h 25 Sep 2002 22:29:47 -0000 1.53 @@ -17,7 +17,7 @@ * You are supposed to compare this with the macro CIRCLEMUD_VERSION() * in utils.h. See there for usage. */ -#define _CIRCLEMUD 0x030015 /* Major/Minor/Patchlevel - MMmmPP */ +#define _CIRCLEMUD 0x030016 /* Major/Minor/Patchlevel - MMmmPP */ /* * If you want equipment to be automatically equipped to the same place @@ -214,7 +214,7 @@ #define PRF_NOGOSS (1 << 19) /* Can't hear gossip channel */ #define PRF_NOGRATZ (1 << 20) /* Can't hear grats channel */ #define PRF_ROOMFLAGS (1 << 21) /* Can see room flags (ROOM_x) */ - +#define PRF_DISPAUTO (1 << 22) /* Show prompt HP, MP, MV when < 30%. */ /* Affect bits: used in char_data.char_specials.saved.affected_by */ /* WARNING: In the world files, NEVER set the bits marked "R" ("Reserved") */ Index: src/sysdep.h =================================================================== RCS file: /home/circledb/.cvs/circle/src/sysdep.h,v retrieving revision 1.40 retrieving revision 1.41 diff -u -p -r1.40 -r1.41 --- src/sysdep.h 16 Apr 2002 02:22:12 -0000 1.40 +++ src/sysdep.h 18 May 2002 00:58:41 -0000 1.41 @@ -347,6 +347,10 @@ struct in_addr { #if defined(CIRCLE_WINDOWS) /* Definitions for Win32 */ +# define snprintf _snprintf +# define vsnprintf _vsnprintf +# define PATH_MAX MAX_PATH + # if !defined(__BORLANDC__) && !defined(LCC_WIN32) /* MSVC */ # define chdir _chdir # pragma warning(disable:4761) /* Integral size mismatch. */ Index: src/utils.c =================================================================== RCS file: /home/circledb/.cvs/circle/src/utils.c,v retrieving revision 1.51 retrieving revision 1.53 diff -u -p -r1.51 -r1.53 --- src/utils.c 9 Apr 2002 15:03:05 -0000 1.51 +++ src/utils.c 26 Sep 2002 00:41:37 -0000 1.53 @@ -297,7 +297,8 @@ void mudlog(int type, int level, int fil */ size_t sprintbit(bitvector_t bitvector, const char *names[], char *result, size_t reslen) { - size_t len = 0, nlen; + size_t len = 0; + int nlen; long nr; *result = '\0'; @@ -545,7 +546,7 @@ int get_line(FILE *fl, char *buf) } -int get_filename(char *filename, size_t fbufsize, int mode, char *orig_name) +int get_filename(char *filename, size_t fbufsize, int mode, const char *orig_name) { const char *prefix, *middle, *suffix; char name[PATH_MAX], *ptr; Index: src/utils.h =================================================================== RCS file: /home/circledb/.cvs/circle/src/utils.h,v retrieving revision 1.56 retrieving revision 1.57 diff -u -p -r1.56 -r1.57 --- src/utils.h 9 Apr 2002 13:09:55 -0000 1.56 +++ src/utils.h 18 May 2002 00:53:03 -0000 1.57 @@ -29,7 +29,7 @@ int dice(int number, int size); size_t sprintbit(bitvector_t vektor, const char *names[], char *result, size_t reslen); size_t sprinttype(int type, const char *names[], char *result, size_t reslen); int get_line(FILE *fl, char *buf); -int get_filename(char *filename, size_t fbufsize, int mode, char *orig_name); +int get_filename(char *filename, size_t fbufsize, int mode, const char *orig_name); time_t mud_time_to_secs(struct time_info_data *now); struct time_info_data *age(struct char_data *ch); int num_pc_in_room(struct room_data *room);