/**************************************************************************/ // duel.cpp - duel combat system implementation - Kal, Dec 99 /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * **************************************************************************/ #include "duel.h" time_t duel_times[6]= { 60, //DS_REQUESTED_OF 60, // DS_WAITING_FOR_REPLY 15, //DS_ABOUT_TO_START, 5*60, //DS_BYPASSINGDUEL 30*60,//DS_DUEL, -1 //DS_PROTECTED - remainding time }; /**************************************************************************/ char *ds_state_name(duel_state st) { switch (st){ case DS_CHALLENGED_BY: return "challenged by"; case DS_WAITING_FOR_REPLY: return "waiting for them to reply"; case DS_ABOUT_TO_START: return "dueling about to activate"; case DS_DUELING: return "dueling"; case DS_CANT_ATTACK: return "can't attack - you can't attack them unless they attack you first"; case DS_SAFE_FROM_BUT_CAN_ATTACK: return "safe - you can attack them, they can't attack you"; case DS_BYPASSINGDUEL: return "bypassing duel"; default: return "BUG - MISSING A STATE IN char *ds_state_name(duel_state st) - PLEASE REPORT!!!"; } } /**************************************************************************/ static char *get_display_pkinfo_time(time_t till) { static char result[MIL]; int diff=(int)(till-current_time); if(diff>15){ sprintf(result, "%d:%02d", diff/60, diff%60); }else{ sprintf(result, " <15s"); } return result; } /**************************************************************************/ // called from in update_handler() on pulse_violence void duel_update() { char_data *dch; for ( dch = player_list; dch; dch = dch->next_player ) { if(dch->duels){ dch->duels->update_duels(dch); } } } /**************************************************************************/ // called from in extract_char when someone logs out void duel_logout(char_data *ch) { char_data *dch; for ( dch = player_list; dch; dch = dch->next_player ) { if(dch->duels){ dch->duels->logout(dch, ch); } } } /**************************************************************************/ // when someone logs out - currently players are allowed to logout // during a duel, victim is the person logging out void duel_data::logout(char_data *ch, char_data *victim) { node=find_char(victim); if(node){ ch->printlnf("`YDuel cancelled with %s because they logged out.`x", PERS(victim, ch)); logf("DUELLOGOUT: %s logged out while active duel with %s (state=%s, time remaining: %s)", victim->name, ch->name, ds_state_name(node->state), get_display_pkinfo_time(node->till)); node->state=DS_REMOVE; node->till=current_time-1; ch->duels->update_duels(ch); } } /**************************************************************************/ void duel_protect_victim(char_data *victim) { char_data *dch; duel_node *node; for ( dch = player_list; dch; dch = dch->next_player ) { if(dch->duels){ node=dch->duels->find_char(victim); if(node && node->state==DS_DUELING){ node->state=DS_CANT_ATTACK; if(victim->duels){ node=victim->duels->find_char(dch); }else{ node=NULL; } if(node){ node->state=DS_SAFE_FROM_BUT_CAN_ATTACK; }else{ bug("duel_protect_victim - failed to do second half!!!"); } } } } victim->println("Your duels have changed to 'safe' until they expire, you attack or steal."); }; /**************************************************************************/ void duel_unprotect_victim(char_data *victim) { char_data *dch; duel_node *node; bool changed=false; for ( dch = player_list; dch; dch = dch->next_player ) { if(dch->duels){ node=dch->duels->find_char(victim); if(node && node->state==DS_CANT_ATTACK){ node->state=DS_DUELING; node=victim->duels->find_char(dch); if(node){ node->state=DS_DUELING; changed=true; }else{ bug("duel_unprotect_victim - failed to do second half!!!"); } } } } if(changed){ victim->println("Your 'safe' duels have all changed to normal."); } }; /**************************************************************************/ void do_acceptduel(char_data *ch, char *argument) { if (IS_OOC(ch)){ ch->println("You can't perform duel related commands in OOC."); return; } if (ch->fighting){ ch->println("You can't use this command while fighting."); return; } if(ch->duels){ ch->duels->accept_duel(ch, argument); return; } ch->println("You have not been challenged to duel recently."); return; }; /**************************************************************************/ void do_declineduel(char_data *ch, char *argument) { if (IS_OOC(ch)){ ch->println("You can't perform duel related commands in OOC."); return; } if (ch->fighting){ ch->println("You can't use this command while fighting."); return; } if(ch->duels){ ch->duels->decline_duel(ch, argument); return; } ch->println("You have not been challenged to duel recently."); return; }; /**************************************************************************/ // used for one player to challenge another to a duel void do_duel(char_data *ch, char *argument) { char arg[MIL]; char_data *victim; if(GAMESETTING4(GAMESET4_DUEL_SYSTEM_DISABLED)){ ch->println("The duel system is disabled."); return; } if (IS_OOC(ch)){ ch->println("You can't perform duel related commands in OOC."); return; } if (ch->fighting){ ch->println("You can't use this command while fighting."); return; } if(!IS_LETGAINED(ch)){ ch->println("You can't use this command unless letgained."); return; } one_argument( argument, arg ); if ( IS_NULLSTR(arg)) { ch->println("Duel whom?"); return; } if ( ( victim = get_char_room( ch, arg ) ) == NULL ) { ch->printlnf("You can't find any '%s' in the current room.", arg); return; } if(victim==ch){ ch->println("You can't duel yourself sorry."); return; } if(IS_NPC(victim)){ ch->println("You can duel only other players."); return; } if(!can_see(victim,ch)){ ch->printlnf("%s can't currently see you, you must go visible to duel them.", CPERS(victim, ch)); return; } if(IS_PEACEFUL(ch) && IS_ACTIVE(victim) && (victim->pcdata->unsafe_due_to_stealing_till<current_time)){ ch->printf("%s is an active player that hasnt' stolen anything in recent times, therefore you can not duel them.\r\n" "Either `=Cbecomeactive`x yourself or use `=Cbypassduel`x.\r\n", CPERS(victim, ch)); return; } if(IS_ACTIVE(ch) && IS_ACTIVE(victim)){ ch->printlnf("%s is not a peaceful player, dueling is not required.", CPERS(victim, ch)); return; } // allocate memory if required if(!ch->duels){ ch->duels=new duel_data(); } if(!victim->duels){ victim->duels=new duel_data(); } // checks about previous duel information if(ch->duels->is_known(victim)){ if(ch->duels->has_been_challenged_by(victim)){ ch->printf("%s has already challenged you to a duel,\r\n" "Use `=Cacceptduel`x to accept their duel.\r\n" "Use `=Cdeclineduel`x to decline their invitation to duel.\r\n", CPERS(victim, ch)); return; }else if(ch->duels->is_waiting_for_reply(victim)){ ch->printf("You have already challenged %s to a duel,\r\n" "You must wait longer to give them a chance to respond before challenging them again.\r\n", PERS(victim, ch)); return; }else if(!ch->duels->is_bypassingduel(victim)){ // if not bypassing - dont let them go futher ch->printlnf("You are already involved in some duel related activities with %s.", PERS(victim, ch)); return; } } // setup duel activities on both characters ch->duels->update_challenge(victim, DS_WAITING_FOR_REPLY); victim->duels->update_challenge(ch, DS_CHALLENGED_BY); ch->duel_challenged++; // do the room/people echos act( "***$n has challenged $N to a duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You challenge $N to a duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n has challenged you to a duel!`x", ch, NULL, victim, TO_VICT ); victim->printf("You may use either `=Cacceptduel`x or `=Cdeclineduel`x to respond...\r\n" "(Of course you can always ignore the challenge if you dont wish to respond)\r\n"); } /**************************************************************************/ // used for one player to bypass the duel system to attack another void do_bypassduel(char_data *ch, char *argument) { char arg[MIL]; char_data *victim; if(GAMESETTING4(GAMESET4_DUEL_SYSTEM_DISABLED)){ ch->println("The duel system is disabled."); return; } if (IS_OOC(ch)){ ch->println("You can't perform duel related commands in OOC."); return; } if (ch->fighting){ ch->println("You can't use this command while fighting."); return; } if(!IS_LETGAINED(ch)){ ch->println("You can't use this command unless letgained."); return; } one_argument( argument, arg ); if ( IS_NULLSTR(arg)) { ch->printf("Bypass the dueling system to attack whom?\r\n" "`RWARNING: `WIf you kill them you will lose a karn!\r\n" " Every 5 times you subdue someone using this system, you lose a karn!`x\r\n"); return; } if ( ( victim = get_char_room( ch, arg ) ) == NULL ) { ch->printlnf("You can't find any '%s' in the current room.", arg); return; } if(victim==ch){ ch->println("You can't bypassduel yourself sorry."); return; } if(IS_NPC(victim)){ ch->println("You can bypassduel only other players."); return; } if(IS_ACTIVE(ch) && IS_ACTIVE(victim)){ ch->printlnf("You do not need to bypass the duelling system to attack %s.", PERS(victim, ch)); return; } // allocate memory if required if(!ch->duels){ ch->duels=new duel_data(); } // checks about previous duel information if(ch->duels->is_known(victim)){ if(ch->duels->has_been_challenged_by(victim)){ ch->printlnf("%s has challenged you to a duel, either ignore/decline this challenge first.", CPERS(victim, ch)); return; }else if(ch->duels->is_waiting_for_reply(victim)){ ch->printf("You have already challenged %s to a duel,\r\n" "You must wait longer to give them a chance to respond before resorting to ignoring the dueling system.\r\n", PERS(victim, ch)); return; }else if(ch->duels->is_bypassingduel(victim)){ ch->printlnf("You are already bypassing the duel system with %s,", PERS(victim, ch)); duel_node *node=ch->duels->find_char(victim); if(node){ // make it possible to increase the ignoreduel timer if(node->till<current_time + 5*60){ ch->printlnf("%s bypass duel timer increased to 5 minutes.", CPERS(victim, ch)); node->till=current_time + 5*60; } } return; }else{ ch->printlnf("You are already involved in some duel related activities with %s.", PERS(victim, ch)); return; } } // setup duel activities on both characters ch->duels->update_challenge(victim, DS_BYPASSINGDUEL); ch->duel_bypass++; ch->printf("You are now bypassing the duel system in regards to %s.\r\n" "`RWARNING: `WIf you kill them you will lose a karn!\r\n" " Every 5 times you subdue someone using the bypass system, you lose a karn!`x\r\n", PERS(victim, ch)); } /**************************************************************************/ void duel_data::display_pkinfo(char_data *ch) { ch->titlebar("DUEL INFO"); ch->println(" Time - Who(status)"); for(node=first; node; node=node->next){ if(node->state!=DS_REMOVE){ ch->printlnf("%5s - %s(%s)", get_display_pkinfo_time(node->till), CPERS(node->victim,ch), ds_state_name(node->state)); } } } /**************************************************************************/ void duel_data::accept_duel(char_data *ch, char *argument) { char arg[MIL]; char_data *victim; one_argument( argument, arg ); // check there is only 1 who is currently challenging them if(IS_NULLSTR(arg)){ duel_node *last=NULL; int count=0; for(node=first; node; node=node->next){ if(node->state==DS_CHALLENGED_BY){ count++; last=node; } } if(count==0){ ch->println("There is currently noone challenging you to a duel."); return; } if(count>1){ ch->printf( "There is more than one person currently challenging you to a duel,\r\n" "please type `=Cacceptduel <name>`x to be more specific.\r\n" "(checkout pkinfo to see who is challenging you).\r\n"); return; } { // single person has challenged - accept them node=last; victim = get_char_room( ch, node->victim->name); if(victim==node->victim){ act( "***$n has accepted $N's challenge to duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You accept $N's duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n accepts your challenge to duel!`x", ch, NULL, victim, TO_VICT ); ch->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); victim->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); // update the ch node node->state=DS_ABOUT_TO_START; node->till=current_time + duel_times[DS_ABOUT_TO_START]; // update the victim node duel_node *temp=victim->duels->find_char(ch); assert(temp!=NULL); temp->state=DS_ABOUT_TO_START; temp->till=current_time + duel_times[DS_ABOUT_TO_START]; } } }else{ victim = get_char_room( ch, arg); if(!victim){ ch->printlnf("You can't seem to find any '%s' here.", arg); return; } if(IS_NPC(victim)){ // idiot check ch->printlnf("%s is not a player, you can duel only other players.", CPERS(victim, ch)); return; } node=find_char(victim); if(node){ if(node->state==DS_CHALLENGED_BY){ act( "***$n has accepted $N's challenge to duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You accept $N's duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n accepts your challenge to duel!`x", ch, NULL, victim, TO_VICT ); ch->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); victim->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); // update the ch node node->state=DS_ABOUT_TO_START; node->till=current_time + duel_times[DS_ABOUT_TO_START]; // update the victim node duel_node *temp=victim->duels->find_char(ch); assert(temp!=NULL); temp->state=DS_ABOUT_TO_START; temp->till=current_time + duel_times[DS_ABOUT_TO_START]; ch->duel_accept++; }else{ ch->printlnf("There is no currently duel CHALLENGE from %s involving you.", PERS(victim, ch)); return; } } ch->printlnf("%s hasn't challenged you to a duel.", CPERS(victim, ch)); } } /**************************************************************************/ void duel_data::decline_duel(char_data *ch, char *argument) { char arg[MIL]; char_data *victim; one_argument( argument, arg ); // check there is only 1 who is currently challenging them if(IS_NULLSTR(arg)){ duel_node *last=NULL; int count=0; for(node=first; node; node=node->next){ if(node->state==DS_CHALLENGED_BY){ count++; last=node; } } if(count==0){ ch->println("There is currently noone challenging you to a duel to decline."); return; } if(count>1){ ch->printf( "There is more than one person currently challenging you to a duel,\r\n" "please type `=Cdeclineduel <name>`x to be more specific.\r\n" "(checkout pkinfo to see who is challenging you).\r\n"); return; } { // single person has challenged - decline them node=last; victim = get_char_room( ch, node->victim->name); if(!victim){ ch->printlnf("%s doesn't appear to be in the room to decline their duel.", CPERS(node->victim, ch)); return; } if(victim==node->victim){ act( "***$n has declined $N's challenge to duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You decline $N's duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n declines your challenge to duel!`x", ch, NULL, victim, TO_VICT ); ch->duel_decline++; node->state=DS_REMOVE; node->till=current_time-1; // update the victim node if(victim->duels){ duel_node *temp=victim->duels->find_char(ch); if(temp){ temp->state=DS_REMOVE; temp->till=current_time-1; } } } } }else{ victim = get_char_room( ch, arg); if(!victim){ ch->printlnf("You can't seem to find any '%s' here.", arg); return; } if(IS_NPC(victim)){ // idiot check ch->printlnf("%s is not a player, you can duel only other players.", CPERS(victim, ch)); return; } node=find_char(victim); if(node){ if(node->state==DS_CHALLENGED_BY){ act( "***$n has declined $N's challenge to duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You decline $N's duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n declines your challenge to duel!`x", ch, NULL, victim, TO_VICT ); ch->duel_decline++; node->state=DS_REMOVE; node->till=current_time-1; // update the victim node duel_node *temp=victim->duels->find_char(ch); assert(temp!=NULL); temp->state=DS_REMOVE; temp->till=current_time-1; }else{ ch->printlnf("There is no currently duel CHALLENGE from %s involving you.", PERS(victim, ch)); return; } } ch->printlnf("%s hasn't challenged you to a duel.", CPERS(victim, ch)); } } /**************************************************************************/ // destructor - deallocate all memory used duel_data::~duel_data() { node=first; while(node){ first=first->next; delete node; node=first; } } /**************************************************************************/ // constructor duel_data::duel_data() { first=NULL; } /**************************************************************************/ // ignore them time has expired duel_node* duel_data::find_char(char_data *victim) { for(node=first; node; node=node->next){ if(node->victim==victim){ return node; } } return NULL; }; /**************************************************************************/ // updates the duels on the current character void duel_data::update_duels(char_data *ch) { assert(ch->duels==this); assert(!IS_NPC(ch)); duel_node *prev; duel_node *node_next; prev=NULL; bool remove=false; // if true, the node is removed for(node=first; node; node=node_next){ node_next=node->next; if(node->till<current_time){ // a timer has expired switch(node->state){ case DS_CHALLENGED_BY: ch->printlnf("`W***You have taken to long to respond to the duel of %s, duel challenge considered ignored.`x", PERS(node->victim, ch)); logf("DUELIGNORE: %s ignored the duel of %s", ch->name, node->victim->name); ch->duel_ignore++; remove=true; break; case DS_WAITING_FOR_REPLY: ch->printlnf("`W***%s hasn't responded to your challenge to a duel, it appears they have ignored you.`x", CPERS(node->victim, ch)); remove=true; break; case DS_ABOUT_TO_START: node->state=DS_DUELING; // go active, but dont tell them node->till=current_time + duel_times[DS_DUELING]; break; case DS_DUELING: case DS_SAFE_FROM_BUT_CAN_ATTACK: case DS_CANT_ATTACK: ch->printlnf("`W***Your duel with %s is over.`x", PERS(node->victim, ch)); remove=true; break; case DS_BYPASSINGDUEL: if(ch->fighting){ ch->printlnf("`R***Your bypassing of the duel system with %s timer has been extended due to combat.`x", PERS(node->victim, ch)); node->till=current_time + 60; }else{ ch->printlnf("`R***Your bypassing of the duel system with %s is over.`x", PERS(node->victim, ch)); remove=true; } break; case DS_REMOVE: // removes a node from the list for any particular reason remove=true; break; default: bug("Unknown state in duel_data::update_duels(char_data *ch)!!!"); do_abort(); } }else if (node->state==DS_CHALLENGED_BY && (IS_ACTIVE(ch) || (ch->pcdata->unsafe_due_to_stealing_till>current_time))) { // PK players autoaccept if victim is in the same room char_data *victim = get_char_room( ch, node->victim->name); if(!victim){ return; } if(victim==node->victim){ duel_node *temp; act( "***$n has accepted $N's challenge to duel!", ch, NULL, victim, TO_NOTVICT ); act( "`W***You accept (automatically) $N's duel!`x", ch, NULL, victim, TO_CHAR ); act( "`W***$n accepts your challenge to duel!`x", ch, NULL, victim, TO_VICT ); ch->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); victim->println("`YPK activities may commence in approximately 15 seconds, and last up to 30 minutes!`x"); // update the ch node node->state=DS_ABOUT_TO_START; node->till=current_time + duel_times[DS_ABOUT_TO_START]; // update the victim node temp=victim->duels->find_char(ch); assert(temp!=NULL); temp->state=DS_ABOUT_TO_START; temp->till=current_time + duel_times[DS_ABOUT_TO_START]; } } // remove the node if necessary if(remove){ if(prev){ prev->next=node->next; }else{ first=node->next; } delete node; }else{ prev=node; } } // delete their duels pointer if it holds nothing if(!first){ delete ch->duels; ch->duels=NULL; } }; /**************************************************************************/ void duel_data::update_challenge(char_data *victim, duel_state to_state) { node=find_char(victim); if(!node){ node=new duel_node; node->flags=0; node->victim=victim; // prepend to list of duels because it is new node->next=first; first=node; } node->state=to_state; node->till=current_time + duel_times[to_state]; } /**************************************************************************/ // return true if they are previously known bool duel_data::is_known(char_data *victim) { node=find_char(victim); if(node){ return true; } return false; } /**************************************************************************/ bool duel_data::has_been_challenged_by(char_data *victim) { node=find_char(victim); if(node){ return (node->state==DS_CHALLENGED_BY); } return false; }; /**************************************************************************/ bool duel_data::is_waiting_for_reply(char_data *victim) { node=find_char(victim); if(node){ return (node->state==DS_WAITING_FOR_REPLY); } return false; }; /**************************************************************************/ bool duel_data::is_about_to_start(char_data *victim) { node=find_char(victim); if(node){ return (node->state==DS_ABOUT_TO_START); } return false; }; /**************************************************************************/ bool duel_data::is_bypassingduel(char_data *victim) { node=find_char(victim); if(node){ return (node->state==DS_BYPASSINGDUEL); } return false; }; /**************************************************************************/ bool duel_data::is_dueling(char_data* ch, char_data *victim){ node=find_char(victim); if(node){ // check if someone is losing their 'safeness' if(node->state==DS_SAFE_FROM_BUT_CAN_ATTACK){ if(victim->duels){ duel_node *temp=victim->duels->find_char(ch); if(temp){ temp->state=DS_DUELING; node->state=DS_DUELING; } } } return (node->state==DS_DUELING); } return false; }; /**************************************************************************/