/* * Version 0.1 (09/01/01) * * a short guide to coding class powers for godwars muds, it is highly recommended that you read * the guide to coding classes first, since we'll be refering to some of the code in that howto. * * This is part 2, You don't need to read part 1 to read this, but it's recommended. * * Howto by Jobo, coder for the Godwars mud Dystopia. */ In this howto I'll try to explain the use of timer-dependent powers, and different ways to implement them. I'll also explain different ways of coding affect-powers, namely powers that affects items and players, ie. web affect, monk deathtouch, vampires sharpen, demon caust, etc. As always, we'll use the Titan class for the examples. First we will code a power for titans, that allows them to unstance the opponent, and give the Titan a timer, so he can't use the power for another 12 mudhours. We will call the power earthquake. void do_earthquake(CHAR_DATA *ch, char *argument) { /* * We'll let the power autotarget the person the Titan is fighting, * it's easier to code, and theres already an example in part 1 that * allows the player to chose his own target. All we need is a pointer * to the victim. */ CHAR_DATA *victim; /* * Again, let's make sure we only have Titans using the command. */ if (IS_NPC(ch)) return; if (!IS_CLASS(ch, CLASS_TITAN)) { send_to_char("Huh?\n\r", ch ); return; } /* * Level needed to make the attack. */ if (ch->pcdata->powers[TITAN_KNOWLEDGE] < 2) { send_to_char("You need level 2 in knowledge before you can use earthquake.\n\r",ch); return; } /* * Here comes the timer part, before we go any further, we need to define a new timer for * this power. Open merc.h, and search for TIMER_, you will find a series of defines for timers, * looking like the following, and the list is ended by a MAX_TIMER define. * (just below you'll find a series of room timers called RTIMER_, these are used when affects * are put into a room (like silence), they can be used in much the same way. #define TIMER_LAYONHANDS 0 #define TIMER_WRENCH 1 #define TIMER_WRENCHED 2 #define TIMER_VAMPCALL 3 #define TIMER_UNCONCIOUS 4 .... #define MAX_TIMER 62 * We want to add a timer called TIMER_EARTHQUAKE, this can be done in two ways. Either we pick one of * the timers that isn't used (like TIMER_SPHINX_ROAR) and rename it to suit our needs, or we make a new * one, moving the MAX_TIMER down one slot. (if you increase MAX_TIMER, you need to recompile the entire * code, or you'll get some strange errors). To figure out whether a certain timer is used or not, you can * do a quick search in the code, and see if the timer is mentioned anywhere besides the update function. grep TIMER_SPHINX_ROAR *.c * The above shell command will search all .c files for the string TIMER_SPHINX_ROAR, and we quickly see that * it's only mentioned in update.c, so we can freely use it. We rename it to TIMER_EARTHQUAKE and edit update.c * to rename the old timer to TIMER_EARTHQUAKE. If you where to add another you should add a message to update.c * telling the player when he can use the power again. It's all located in the function char_update(), just below * this code : for (i = 0; i < MAX_TIMER; i++) if (ch->tick_timer[i] > 0) ch->tick_timer[i] --; * Now that we have our new timer defined, and it updates correct in the update function, we should make a * quick check to see if the players timer has run out, to see if he can actually use the power. */ if (!TIME_UP(ch, TIMER_EARTHQUAKE)) { send_to_char("This power can only be used every 12 hours.\n\r", ch); return; } /* * Let's make sure we have a target to use the power on. */ if ((victim = ch->fighting) == NULL) { send_to_char("You are not fighting anyone.\n\r", ch); return; } /* * We have a target, so let's give the player a new timer, so he can't use it for yet another 12 mudhours. */ SET_TIMER(ch, TIMER_EARTHQUAKE, 12); /* * We unstance the target. */ do_stance(victim, ""); /* * Done. */ return; } Timers are one way to prevent powerful commands being used over and over without having to give a huge WAIT_STATE, but we have other ways of doing the same, though we'll have to create the entire thing from scratch, and it won't look nearly as nice (The do_powerwords command for the Undead Knight class that Dystopia use is one example of this. Here we have 4 different powerwords, each giving a timer of 2-5 rounds, depending on which powerword is used. We don't lag the player with WAIT_STATE, but rather makes sure the command cannot be used untill the timer runs out). I'm not going to give an example of this, if you want to code something similar, you should look at undead_knigt.c in the dystopian code. I used the update function for undead knights to decrease the timer one point each round, this code can be found in update.c in the function update_knight(). The single line needed looks something like this : if (ch->pcdata->powers[POWER_TICK] > 0) ch->pcdata->powers[POWER_TICK]--; Now let's take a look at commands that gives affects to players and items. First a way to give affects to items. Unfortunatly the commands already coded that gives affect to items are rather poorly done, relying on a function that was never ment to be used for this. Good examples of poorly coded functions are do_bloodagony, which uses the function oset_affect() which was made for questing values on items (it also sets a bit, to prevent the same affect being quested on the item twice). First let's code a quick command that allows us to set affects on items without having to bother with special bits. void affect_to_obj(OBJ_DATA *obj, AFFECT_DATA *paf) { AFFECT_DATA *paf_new; if (affect_free == NULL) { paf_new = alloc_perm( sizeof(*paf_new) ); } else { paf_new = affect_free; affect_free = affect_free->next; } *paf_new = *paf; paf_new->next = obj->affected; obj->affected = paf_new; return; } You can place the function in whatever file you like (handler.c would be a good choice), just remember to define it in merc.h, so we can use it in other files as well. void affect_to_obj args ((OBJ_DATA *obj, AFFECT_DATA *paf)); We now have the tools needed to make a command that gives an affect to an object. We'll make a rather rigged command, just to show what we can actually do with this nifty new function affect_to_obj(). void do_titancraft(CHAR_DATA *ch, char *argument) { /* * We are in need of a pointer to the object we want to modify, as well as an argument given by the * player, so we can locate the item. We also need an affect, we will modify the affect later, and * send it to the item via our new function affect_to_obj(). */ OBJ_DATA *obj; AFFECT_DATA paf; char arg[MAX_STRINGLENGTH]; /* * Titans only.... */ if (IS_NPC(ch)) return; if (!IS_CLASS(ch, CLASS_TITAN)) { send_to_char("Huh?\n\r", ch ); return; } /* * we snatch the argument. */ one_argument(argument, arg); /* * Let's see if we can find the item, the player should have it in his inventory. */ if ((obj = get_obj_carry(ch, arg)) == NULL) { send_to_char("You dont have that item.\n\r",ch); return; } /* * We don't want to modify items that aren't a weapon or a piece of armor, * so we need to check for this. */ if (obj->item_type != ITEM_WEAPON && obj->item_type != ITEM_ARMOR) { send_to_char("You can only affect weapons and armor.\n\r", ch); return; } /* * We don't want the player to be able to superrig the item, thus the power should only * be usable once on each item. For this we need to define a new QUEST_ bit, so lets open * merc.h and define one (just search merc.h for QUEST_). Scrolling through the list, we find * a free spot at 67108864 (remember it's a bitvector). so we define QUEST_TITANCRAFT as this value. */ if (IS_SET(obj->quest, QUEST_TITANCRAFT)) { send_to_char("This item is already titancrafted.\n\r", ch); return; } /* * It's time to define the affect we want to give the object, you have to fill out all the values, * or you might accidently cause a crash. The .type value isn't important, so just set it to 0, * and the duration should be -1 to avoid it fading away (I doubt affect on items can fade, but let's * stay on the safe side). As for the .bitvector value, just est it to 0 as well. The only two important * values are .modifier which decides how good the affect is, and the .location which decides what * kind of affect we want to give the item * * The possibly locations we can chose among is : * * APPLY_STR, APPLY_DEX, APPLY_INT, APPLY_WIS, APPLY_CON, * APPLY_MANA, APPLY_HIT, APPLY_MOVE * APPLY_AC, APPLY_HITROLL, APPLY_DAMROLL * * each of the above gives a modifier (the amount is defined in the .modifier value) to the mentioned stat. */ /* * Here we define an affect, and send it the the object. * First a good modifier of 20 to hitroll. */ paf.type = 0; paf.duration = -1; paf.location = APPLY_HITROLL; paf.modifier = 20; paf.bitvector = 0; affect_to_obj(obj, &paf); /* * And for damroll we give another 20. */ paf.type = 0; paf.duration = -1; paf.location = APPLY_DAMROLL; paf.modifier = 20; paf.bitvector = 0; affect_to_obj(obj, &paf); /* * We could continue stacking up on affects with this power, but lets end it here, * a +20/+20 modifier to hitroll/damroll is powerful enough for a simple class command. */ /* * Finally we tell the player everything went fine, and set the QUEST_TITANCRAFT bit on * the item, so the power cannot be used again. */ SET_BIT(obj->quest, QUEST_TITANCRAFT); send_to_char("Ok.\n\r", ch); /* * done. */ return; } Another type of commands, is powers that affects players, sending an affect to that player. We will take a look at the web command, and try to code it in two different ways. Heres example one, which is the same way it's done in normal godwars. void do_web(CHAR_DATA *ch, char *argument) { CHAR_DATA *victim; char arg [MAX_INPUT_LENGTH]; int sn; int level; int spelltype; one_argument( argument, arg ); /* * And a check to see if the player is a mob or a player of the wrong class. * Code left out, since it's not really important. */ if ((victim = get_char_room(ch, arg)) == NULL) { send_to_char("They aren't here.\n\r", ch); return; } if (ch == victim) { send_to_char("You cannot web yourself.\n\r", ch); return; } /* * All of the above code is standard, and the only difference between the two * ways to code this is in the following. */ /* * Here we snatch the #id if the 'web' skill, using the function skill_lookup(). * skill_lookup() returns -1 on failure, so we return if the skill doesn't exist. */ if ((sn = skill_lookup( "web" )) < 0) return; /* * The next two lines decides how good the player is at using the skill, all skills * counts as one of the five spellcolors (red/blue/green/yellow/purple), and the level * of the webspell we let the player cast, depends on his level in that color. */ spelltype = skill_table[sn].target; level = ch->spl[spelltype] * 0.25; /* * The following line casts the spell 'web' on the target, and then spell_web() in * magic.c takes over, deciding whether the the web hits or not, and sending all the * correct messages to all players involved. This is a nice and easy way to do things, * but we have very little control over the succesrate of our skill. */ (*skill_table[sn].spell_fun) ( sn, level, ch, victim ); /* * And ofcourse a WAIT_STATE of one round. */ WAIT_STATE( ch, 12 ); /* * done. */ return; } So what can we do to let use decide the succesrate of the web power ?? Reusing all the above code, except everything from the following line and down : // if ((sn = skill_lookup( "web" )) < 0) return; /* * The alternative code. * We need to define an AFFECT_DATA variable, but we don't need the three integers anymore. CHAR_DATA *victim; char arg [MAX_INPUT_LENGTH]; AFFECT_DATA af; */ /* * We set the WAIT_STATE of one round already here, so any attempt, succesful or not * will result in 1 round lag. */ WAIT_STATE(ch, 12); /* * Now let's send a message, telling everyone about the players attempt to web his target. * The next three lines are stolen direcly from spell_web() in magic.c */ act("You point your finger at $N and a web flies from your hand!",ch,NULL,victim,TO_CHAR); act("$n points $s finger at $N and a web flies from $s hand!",ch,NULL,victim,TO_NOTVICT); act("$n points $s finger at you and a web flies from $s hand!",ch,NULL,victim,TO_VICT); /* * First we need to decide whether the target is already webbed, in * which case we shouldn't do anything. */ if (IS_SET(victim->affected_by, AFF_WEBBED)) { send_to_char("They are already webbed.\n\r", ch); return; } /* * Now we have all the freedom we need to decide the succesrate, so we set it to 40% * Again the messages are stolen from spell_web() since I'm to lazy to come up with new ones. */ if (number_percent() > 40) { send_to_char("You dodge the web!\n\r", victim); act("$n dodges the web!",victim, NULL, NULL, TO_ROOM); return; } /* * Succes, so let's set the affect on the player, and send a message about it. * No suprise here, I've stolen the messages from spell_web() again. * * Notice that we are doing the lookup for the webskill without checking to see * if the return value is -1, this isn't to smart, since the mud would crash if * the web skill doesn't exists.... I would suggest checking for that first. */ af.type = skill_lookup("web"); af.location = APPLY_AC; af.modifier = 200; af.duration = number_range(1,2); af.bitvector = AFF_WEBBED; affect_to_char( victim, &af ); send_to_char( "You are coated in a sticky web!\n\r", victim ); act("$n is coated in a sticky web!",victim,NULL,NULL,TO_ROOM); /* * Done. * return; } The code doesn't look nearly as easy to read in the last example, but we got the option of deciding the succesrate ourself. We could have modified the function spell_web() instead, and increased/decreased the succesrate in that function instead, its all up to you. You will notice that we use the function affect_to_char() in the last example, which is a function that does almost the same as affect_to_obj, but here it's _vital_ that we define the .type value, because we need the affect to have a name. If the affect doesn't have a .type, then the mud will crash whenever we try to check the type of the affect. ie. should a player use the 'affects' command to see what he is currently affected by, he would see a list of affects by their name (.type), should we not have defined the type, the mud will crash. Good luck! Jobo