Crossfire Client, Branch  R11627
player.c
Go to the documentation of this file.
00001 const char * const rcsid_common_player_c =
00002     "$Id: player.c 9201 2008-06-01 17:32:45Z anmaster $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 
00026 #include <client.h>
00027 #include <external.h>
00028 #include <script.h>
00029 
00030 /* This file handles various player related functions.  This includes
00031  * both things that operate on the player item, cpl structure, or
00032  * various commands that the player issues.
00033  *
00034  *  This file does most of the handling of commands from the client to
00035  *  server (see commands.c for server->client)
00036  *
00037  *  does most of the work for sending messages to the server
00038  *   Again, most of these appear self explanatory.  Most send a bunch of
00039  *   commands like apply, examine, fire, run, etc.  This looks like it
00040  *   was done by Mark to remove the old keypress stupidity I used.
00041  */
00042 
00043 /* This translates the numeric direction id's into the actual direction
00044  * commands.  This lets us send the actual command (ie, 'north'), which
00045  * makes handling on the server side easier.
00046  */
00047 
00048 const char *const directions[9] = {"stay", "north", "northeast", "east", "southeast",
00049                 "south","southwest", "west", "northwest"};
00050 
00051 
00052 /*
00053  *  Initialiazes player item, information is received from server
00054  */
00055 void new_player (long tag, char *name, long weight, long face)
00056 {
00057     Spell *spell, *spnext;
00058 
00059     cpl.ob->tag    = tag;
00060     cpl.ob->nrof   = 1;
00061     copy_name (cpl.ob->d_name, name);
00062     cpl.ob->weight = (float) weight / 1000;
00063     cpl.ob->face   = face;
00064 
00065     if (cpl.spelldata) {
00066         for (spell = cpl.spelldata; spell; spell = spnext) {
00067             spnext = spell->next;
00068             free(spell);
00069         }
00070         cpl.spelldata = NULL;
00071     }
00072 
00073 }
00074 
00075 void look_at(int x, int y)
00076 {
00077     cs_print_string(csocket.fd, "lookat %d %d", x, y);
00078 }
00079 
00080 void client_send_apply (int tag)
00081 {
00082     cs_print_string(csocket.fd, "apply %d", tag);
00083 }
00084 
00085 void client_send_examine (int tag)
00086 {
00087     cs_print_string(csocket.fd, "examine %d", tag);
00088 
00089 }
00090 
00091 /* Requests nrof objects of tag get moved to loc. */
00092 void client_send_move (int loc, int tag, int nrof)
00093 {
00094     cs_print_string(csocket.fd, "move %d %d %d", loc, tag, nrof);
00095 }
00096 
00097 
00098 
00099 void move_player(int dir) {
00100     /* Should we perhaps use the real repeat count here? */
00101     send_command(directions[dir], -1, SC_NORMAL);
00102 }
00103 
00104 
00105 /* Fire & Run code.  The server handles repeating of these actions, so
00106  * we only need to send a run or fire command for a particular direction
00107  * once - we use the drun and dfire to keep track if we need to send
00108  * the full command.
00109  */
00110 static int drun=-1, dfire=-1;
00111 
00112 /* Fires in a specified direction.  Note that direction 0 is a valid
00113  * case - the fire is centered on the player.
00114  */
00115 
00116 void stop_fire(void)
00117 {
00118     if (cpl.input_state != Playing) return;
00119     dfire |= 0x100;
00120 }
00121 
00122 void clear_fire_run(void)
00123 {
00124     if ((dfire!=-1) && (dfire & 0x100)) {
00125         send_command("fire_stop", -1, SC_FIRERUN);
00126         dfire=-1;
00127     }
00128     if ((drun!=-1) && (drun & 0x100)) {
00129         send_command("run_stop", -1, SC_FIRERUN);
00130         drun=-1;
00131     }
00132 }
00133 
00134 void clear_fire(void)
00135 {
00136     if (dfire!=-1) {
00137         send_command("fire_stop", -1, SC_FIRERUN);
00138         dfire=-1;
00139     }
00140 }
00141 
00142 void clear_run(void)
00143 {
00144     if (drun!=-1) {
00145         send_command("run_stop", -1, SC_FIRERUN);
00146         drun=-1;
00147     }
00148 }
00149 
00150 
00151 void fire_dir(int dir) {
00152     char buf[MAX_BUF];
00153 
00154     if (cpl.input_state != Playing) return;
00155     if (dir!= dfire) {
00156         snprintf(buf, sizeof(buf), "fire %d", dir);
00157         if (send_command(buf, cpl.count, SC_NORMAL)) {
00158             dfire=dir;
00159             cpl.count=0;
00160         }
00161     } else {
00162         dfire &= 0xff;  /* Mark it so that we need a stop_fire */
00163     }
00164 }
00165 
00166 void stop_run(void)
00167 {
00168     send_command("run_stop", -1, SC_FIRERUN);
00169     drun |= 0x100;
00170 }
00171 
00172 void run_dir(int dir) {
00173     char buf[MAX_BUF];
00174 
00175     if (dir!=drun) {
00176         snprintf(buf, sizeof(buf), "run %d", dir);
00177         if (send_command(buf, -1, SC_NORMAL))
00178             drun=dir;
00179     } else {
00180         drun &= 0xff;
00181     }
00182 }
00183 
00184 
00185 /* This should be used for all 'command' processing.  Other functions should
00186  * call this so that proper windowing will be done.
00187  * command is the text command, repeat is a count value, or -1 if none
00188  * is desired and we don't want to reset the current count.
00189  * must_send means we must send this command no matter what (ie, it is
00190  * an administrative type of command like fire_stop, and failure to send
00191  * it will cause definate problems
00192  * return 1 if command was sent, 0 if not sent.
00193  */
00194 
00195 int send_command(const char *command, int repeat, int must_send) {
00196     static char last_command[MAX_BUF]="";
00197 
00198     script_monitor(command,repeat,must_send);
00199     if (cpl.input_state==Reply_One) {
00200         LOG(LOG_ERROR,"common::send_command","Wont send command '%s' - since in reply mode!",
00201                 command);
00202         cpl.count=0;
00203         return 0;
00204     }
00205 
00206     /* Does the server understand 'ncom'? If so, special code */
00207     if (csocket.cs_version >= 1021) {
00208         int commdiff=csocket.command_sent - csocket.command_received;
00209 
00210         if (commdiff<0) commdiff +=256;
00211 
00212         /* if too many unanswered commands, not a must send, and command is
00213          * the same, drop it
00214          */
00215         if (commdiff>use_config[CONFIG_CWINDOW] && !must_send && !strcmp(command, last_command)) {
00216             if (repeat!=-1) cpl.count=0;
00217             return 0;
00218 #if 0 /* Obnoxious warning message we don't need */
00219             fprintf(stderr,"Wont send command %s - window oversized %d %d\n",
00220                     command, csocket.command_sent, csocket.command_received);
00221 #endif
00222         }
00223         else {
00224             SockList sl;
00225             uint8 buf[MAX_BUF];
00226 
00227             /* Don't want to copy in administrative commands */
00228             if (!must_send) strcpy(last_command, command);
00229             csocket.command_sent++;
00230             csocket.command_sent &= 0xff;   /* max out at 255 */
00231 
00232             SockList_Init(&sl, buf);
00233             SockList_AddString(&sl, "ncom ");
00234             SockList_AddShort(&sl, csocket.command_sent);
00235             SockList_AddInt(&sl, repeat);
00236             SockList_AddString(&sl, command);
00237             SockList_Send(&sl, csocket.fd);
00238         }
00239     } else {
00240         cs_print_string(csocket.fd, "command %d %s", repeat,command);
00241     }
00242     if (repeat!=-1) cpl.count=0;
00243     return 1;
00244 }
00245 
00246 void CompleteCmd(unsigned char *data, int len)
00247 {
00248     if (len !=6) {
00249         LOG(LOG_ERROR,"common::CompleteCmd","Invalid length %d - ignoring", len);
00250         return;
00251     }
00252     csocket.command_received = GetShort_String(data);
00253     csocket.command_time = GetInt_String(data+2);
00254     script_sync(csocket.command_sent - csocket.command_received);
00255 }
00256 
00257 
00258 
00259 /* This does special processing on the 'take' command.  If the
00260  * player has a container open, we want to specifiy what object
00261  * to move from that since we've sorted it.  command is
00262  * the command as tped, cpnext is any optional params.
00263  */
00264 void command_take (const char *command, const char *cpnext)
00265 {
00266     /* If the player has specified optional data, or the player
00267      * does not have a container open, just issue the command
00268      * as normal
00269      */
00270     if (cpnext || cpl.container == NULL)
00271         send_command(command, cpl.count, 0);
00272     else {
00273         if (cpl.container->inv == NULL)
00274             draw_info("There is nothing in the container to move", NDI_BLACK);
00275         else
00276             cs_print_string(csocket.fd,"move %d %d %d", cpl.ob->tag,
00277                 cpl.container->inv->tag, cpl.count);
00278     }
00279 }