/* * Copyright (C) 1999 Peter Amstutz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of *the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "tcpcore.h" #include "relay.h" #include "terrain.h" #include "gfx.h" #include "clihandlers.h" #include "packets.h" #include "text.h" #include "ballistics.h" #include "weapons/weapon.h" #include "weapons/weapongfx.h" #include "game.h" #include "kclient.h" #include "cfgfile.h" #include "log.h" #include "demo.h" #include "../config.h" #define DEFAULT_PORT 8086 #define DEFAULT_SERVER "localhost" #define HELP_TEXT "Syntax %s:\n"\ "Options:\n"\ " -nSTR nickname\n" \ " -fSTR configuration file to use (default %s)\n" \ " -sSTR server to connect to\n" \ " -pINT port to connect to (default 8086)\n" \ " -xINT horizontal resolution (default 640)\n" \ " -yINT vertical resolution (default 480)\n" \ " -d record a demo\n" \ " -o[f] observe a demo (of = faster)\n" \ " -l logging level\n" Relay_rl *relay; char gm_quit = 0; int svrid, gm_myid = -1; Gamemode_gm gm_gamemode = NOTPLAYING; Gametype_gm gm_gametype = SIMULTANEOUS; int gm_activeplayers = 0; int gm_currentRound = -1; int gm_totalRounds = -1; Player_pl *gm_myplstruct; ListBox_txt *wpnlist; ItemStock_pl *gm_curitem; char gm_tank_damaged = 0; char gm_ctf = 0; char gm_stuff_happening; char gm_AS_in_queue; int gm_AS_queue[256] = { 0 }; int gm_AS_pos = 0; int gm_WS_queue[256] = { 0 }; int gm_WS_pos = 0; int gm_death_queue[256] = { 0 }; int gm_dq_pos = 0; int gm_chatlines = 0; int gm_statusArea=0; int gm_def_armor = 100; int demo = 0; int observe = 0; int last_wind=0; int curShooterId=0; int gm_iAmServer = 0; /* total waste of mem */ Player_pl *gm_firing_order[10]; void clDrawWeaponElement(int x, int y, int w, int h, void *elem) { char *wpname = (char *) elem; int n; Weapon_wep *wp; wp = wepLookupWeapon(wpname); assert(wp); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(x, y, "%s $%i/%i (%i)", wpname, wp->cost * wp->count, wp->count, (n = plCountWeaponInStock(gm_myplstruct, wp)) > 99 ? 99 : n); } int clBuyWeapon(void *elem) { struct BuyWeapon_pkt bw; char buf[512]; bw.type[0] = 'B'; bw.type[1] = 'W'; bw.count = wepLookupWeapon((char *) elem)->count; strcpy(bw.weapontype, (char *) elem); rlSend(relay, svrid, buf, pktPackBuyWeapon(buf, &bw)); ch_weaponbuylock = 1; while(ch_weaponbuylock && gm_gamemode == PREGAME) rlMain(relay, NULL); return 1; } int clSellWeapon(void *elem) { struct BuyWeapon_pkt bw; char buf[512]; bw.type[0] = 'S'; bw.type[1] = 'W'; bw.count = 1; strcpy(bw.weapontype, (char *) elem); rlSend(relay, svrid, buf, pktPackBuyWeapon(buf, &bw)); ch_weaponbuylock = 1; while(ch_weaponbuylock && gm_gamemode == PREGAME) rlMain(relay, NULL); return 1; } void clFireWeapon(void *info) { struct FireCmd_pkt sht; char buf[512]; sht.type[0] = 'F'; sht.type[1] = 'S'; sht.a = gm_myplstruct->fire_angle; sht.v = gm_myplstruct->fire_velocity; strcpy(sht.shottype, ((Weapon_wep *) gm_curitem->info)->name); rlSend(relay, svrid, buf, pktPackFireCmd(buf, &sht)); } void clInitialize(int argc, char **argv) { int sock = -1, b; char *inp = (char *) malloc(256); char *server = NULL; char *name = NULL; char *configfile = NULL; int port = DEFAULT_PORT; int go; Weapon_wep *tmp; ggi_event ev; char nset = 0, sset = 0, xset = 0, yset = 0, pset = 0, lset = 0, fset = 0; const char *log_str[] = { "critical", "interesting", "debug", "spam", NULL }; const Levels_log log_val[] = { CRITICAL, INTERESTING, DEBUG, SPAM }; int observeSpeed = NORMAL; int chatY, chatH; logPrintf(INTERESTING, "King of the Hill (KOTH) Client version %s\n", VERSION); logPrintf(INTERESTING, "Copyright (C) 1999 Peter Amstutz\n"); logPrintf(INTERESTING, "Copyright (C) 2002, 2003 Allan Douglas\n"); logPrintf(INTERESTING, "KOTH comes with ABSOLUTELY NO WARRANTY\n"); logPrintf(INTERESTING, "This is free software, and you are welcome to redistribute it\n"); logPrintf(INTERESTING, "under the conditions of the GNU GPL\n"); while(1) { go = getopt(argc, argv, "hn:s:x:y:p:f:l:do::"); if(go == EOF) break; switch (go) { case 'n': name = strdup(optarg); nset = 1; break; case 's': server = strdup(optarg); sset = 1; break; case 'x': gfx_xmax = atoi(optarg); xset = 1; break; case 'y': gfx_ymax = atoi(optarg); yset = 1; break; case 'p': port = atoi(optarg); pset = 1; break; case 'f': configfile = strdup(optarg); fset = 1; break; case 'l': lset = 1; if(strcmp(optarg, "critical") == 0) log_level = CRITICAL; else if(strcmp(optarg, "interesting") == 0) log_level = INTERESTING; else if(strcmp(optarg, "debug") == 0) log_level = DEBUG; else if(strcmp(optarg, "spam") == 0) log_level = SPAM; else { lset = 0; logPrintf(CRITICAL, "Bad log level %s\n", optarg); } break; case 'h': logPrintf(CRITICAL, HELP_TEXT, argv[0], DEFAULT_CONFIGFILE); exit(0); break; case 'd': demo = 1; break; case 'o': observe = 1; if(optarg == 0) observeSpeed = NORMAL; else if(strcmp(optarg, "f") == 0) observeSpeed = FAST; else observeSpeed = NORMAL; break; default: logPrintf(CRITICAL, HELP_TEXT, argv[0], DEFAULT_CONFIGFILE); exit(1); break; } } /* * if(configfile==NULL) configfile=DEFAULT_CONFIGFILE; */ cfg_configuration = cfgReadConfiguration(configfile); if(fset) free(configfile); if(cfg_configuration != NULL) { if(!sset) { if(!cfgLoadConfigItemStr(cfg_configuration, "server.host", &server)) { logPrintf(CRITICAL, "No server name found in config file,\n" "trying default %s\n", DEFAULT_SERVER); } } if(!pset) cfgLoadConfigItemInt(cfg_configuration, "server.port", &port); if(!xset) cfgLoadConfigItemInt(cfg_configuration, "client.xsize", &gfx_xmax); if(!yset) cfgLoadConfigItemInt(cfg_configuration, "client.ysize", &gfx_ymax); if(!lset) cfgLoadConfigItemOption(cfg_configuration, "client.logging", log_str, (int *) log_val, (int *) &log_level); cfgLoadConfigItemInt(cfg_configuration, "tank.armor", &gm_def_armor); cfgLoadConfigItemInt(cfg_configuration, "client.chatlines", &gm_chatlines); cfgLoadConfigItemInt(cfg_configuration, "client.statusarea", &gm_statusArea); cfgLoadConfigItemInt(cfg_configuration, "client.armorbar", &gfx_armorBar); } else { if(!pset) port = DEFAULT_PORT; if(!xset) gfx_xmax = 640; if(!yset) gfx_ymax = 480; } gfxInit(); txtInit(); wepInit(); wgxInit(); pl_tankwidth = ter_sizex / TANKSCREENRATIO_X; pl_tankheight = ter_sizey / TANKSCREENRATIO_Y; ch_scrlwin = txtMakeScrollWindow(gfx_xmax / 64, gfx_ymax / 2 + 3, (gfx_xmax * 7) / 10, gfx_ymax / 2 - (20 + gfx_ymax / 48)); inp[0] = 0; ch_inpbox = txtMakeInputBox(gfx_xmax / 64, gfx_ymax - (20 + gfx_ymax / 48), (gfx_xmax * 7) / 10, 20, 0, inp, 255); ch_postinpbox = txtMakeInputBox(3, gfx_ymax - 22, gfx_xmax - 6, 20, 0, inp, 255); if(gm_chatlines > 0) { chatY=gfx_ymax-(gm_chatlines+1)*20; chatH=gm_chatlines*20; gfx_ysize-=(gm_chatlines+1)*20; ch_ingmmsg=txtMakeScrollWindow(3, chatY, gfx_xmax-6, chatH); ch_ingameinpbox = ch_postinpbox; } else { chatY=30; chatH=100; ch_ingmmsg=txtMakeScrollWindow(gfx_xmax/64, chatY, gfx_xmax-gfx_xmax/32, chatH); ch_ingameinpbox = ch_inpbox; } /* if the status area is enabled */ if(gm_statusArea == 1) { /* steal some space from the Y size... ((#lines * font height) + pad) */ gfx_ysize-=ALLPLSTATUS_H; } ch_postgmsg = txtMakeScrollWindow(3, gfx_ymax - 105, gfx_xmax, 80); wpnlist = txtMakeListBox(gfx_xmax / 64, 61, (gfx_xmax * 7) / 10, gfx_ymax / 2 - 40, clBuyWeapon, clSellWeapon, clDrawWeaponElement); tmp = wep_WeaponList; do { if(tmp->cost > 0) txtAddToListBox(wpnlist, tmp->name); tmp = tmp->next; } while(tmp != wep_WeaponList); gfxInitTankImage(); balInit(); if(observe == 1) { demoPlayDemo(observeSpeed); } else { /* * should really stop the demo somewhere... XXX */ if(demo == 1) { demoRecordDemo(); } logPrintf(DEBUG, "not observing\n"); if(!sset) { logPrintf(DEBUG, "!sset\n"); server = strdup(DEFAULT_SERVER); ggiSetGCForeground(gfx_vis, gfx_white); txtScrollWindowPrintf(ch_scrlwin, "Please enter a server, or enter to use (%s):", server); gfxUpdate(); gfxDoUpdate(); while(!sset) { ggiEventRead(gfx_vis, &ev, emKeyPress | emKeyRepeat | emExpose); switch (ev.any.type) { case evKeyPress: case evKeyRepeat: switch (ev.key.sym) { case GIIUC_Return: if(*ch_inpbox->string != 0) { free(server); server = strdup(ch_inpbox->string); } txtClearInputBox(ch_inpbox); gfxUpdate(); sset = 1; break; default: ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); break; } case evExpose: gfxUpdate(); break; } gfxDoUpdate(); } } if((sock = tcpDial(server, port)) < 0) { logPrintf(CRITICAL, "Server not responding\n"); exit(-1); } } if(sset) free(server); if((relay = rlInit(-1)) == NULL) { logPrintf(CRITICAL, "Error initializing relay code"); exit(-1); } svrid = rlAddConnection(relay, sock); rlSend(relay, svrid, "NP", 2); memset(ter_data, 0, sizeof(ter_data)); rlRegisterHandler(relay, "UT", chUpdateTerrain); /* chUpdateTerrain() */ rlRegisterHandler(relay, "NT", chNewTerrain); /* chNewTerrain() */ rlRegisterHandler(relay, "GM", chSetGameMode); /* chSetGameMode() */ rlRegisterHandler(relay, "GT", chSetGameType); /* chSetGameType() */ rlRegisterHandler(relay, "MS", chMessage); /* chMessage() */ rlRegisterHandler(relay, "MC", chMessage); /* chMessage() */ rlRegisterHandler(relay, "UR", chGetMyID); /* chGetMyID() */ rlRegisterHandler(relay, "NP", chNewPlayer); /* chNewPlayer() */ rlRegisterHandler(relay, "ST", chSetTank); /* chSetTank() */ rlRegisterHandler(relay, "SD", chSetTank); /* chSetTank() */ rlRegisterHandler(relay, "SF", chShotFired); /* chShotFired() */ rlRegisterHandler(relay, "CR", chSetReadiness); /* chChangeReadiness() */ rlRegisterHandler(relay, "SN", chSetName); /* chSetName() */ rlRegisterHandler(relay, "RP", chRemovePlayer); /* chRemovePlayer() */ rlRegisterHandler(relay, "AS", chActivateShots); /* chActivateShots() */ rlRegisterHandler(relay, "BW", chBuyWeapon); /* chBuyWeapon() */ rlRegisterHandler(relay, "SW", chSellWeapon); /* chSellWeapon() */ rlRegisterHandler(relay, "SM", chSetMoney); /* chSetMoney() */ rlRegisterHandler(relay, "PV", chCheckProtocolVersion); /* chCheckProtocolVersion() */ rlRegisterHandler(relay, "WS", chSetWindSpeed); /* chSetWindSpeed() */ rlRegisterHandler(relay, "WT", chSetWallType); /* chSetWallType() */ rlRegisterHandler(relay, "US", chUpdateScore); /* chUpdateScore() */ rlRegisterHandler(relay, "NR", chUpdateRound); /* chUpdateRound() */ rlRegisterHandler(relay, "TR", chUpdateTotalRounds); /* chUpdateTotalRounds() */ rlRegisterHandler(relay, "UF", chUpdateFireInfo); /* chUpdateFireInfo() */ rlRegisterHandler(relay, "TC", chTankColor); /* chTankColor() */ rlSetDisconnectFunc(relay, chQuit); if((demo == 1) || (observe == 1)) { rlSendTyped(relay, svrid, "SN", "", 1); } else if(!nset) { ggiSetGCForeground(gfx_vis, gfx_white); txtScrollWindowPrintf(ch_scrlwin, "Please enter the name you wish to play as, " "or leave blank and hit enter to observe:"); gfxUpdate(); gfxDoUpdate(); for(b = 0; !b;) { ggiEventRead(gfx_vis, &ev, emKeyPress | emKeyRepeat | emExpose); switch (ev.any.type) { case evKeyPress: case evKeyRepeat: switch (ev.key.sym) { case GIIUC_Return: name = strdup(ch_inpbox->string); txtClearInputBox(ch_inpbox); gfxUpdate(); b = 1; break; default: ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); break; } case evExpose: gfxUpdate(); break; } gfxDoUpdate(); } } if(name) { rlSendTyped(relay, svrid, "SN", name, strlen(name) + 1); free(name); } if(!observe) { char buf[256]; struct ChangeReady_pkt cr; cr.type[0] = 'C'; cr.type[1] = 'R'; cr.id = gm_myid; cr.r = (ubyte_pkt)NOTREADY; rlSend(relay, svrid, buf, pktPackChangeReady(buf, &cr)); } } void clPregame() { struct timeval tv; ggi_event ev; ggi_color grey; /* * pregame */ /* ** get rid of any weapons if the game is over ** ** check for either current round is 1 or total rounds to cover ** both possible cases of a race condition */ if(((gm_currentRound > gm_totalRounds) || (gm_currentRound == 1)) && gm_totalRounds>0) { plClearAllWeapon(gm_myid); } ggiSetGCForeground(gfx_vis, 0); ggiFillscreen(gfx_vis); ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, 5, 40, 240, 20); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(0, 0, "King of the Hill (KOTH) Client version %s", VERSION); txtPrintf(0, 20, "Alt-h or Meta-h for keys help"); txtPrintf(5, 40, "$%i : ROUND %d of %d", gm_myplstruct->money, gm_currentRound, gm_totalRounds); txtClearScrollWindow(ch_scrlwin); grey.r = 0x8888; grey.g = 0x8888; grey.b = 0x8888; ggiSetGCForeground(gfx_vis, ggiMapColor(gfx_vis, &grey)); ggiDrawHLine(gfx_vis, 0, 39, (gfx_xmax * 7) / 10 + gfx_xmax / 64); ggiDrawHLine(gfx_vis, 0, 60, (gfx_xmax * 7) / 10 + gfx_xmax / 64); ggiDrawHLine(gfx_vis, 0, gfx_ymax / 2 + 1, (gfx_xmax * 7) / 10 + gfx_xmax / 64); ggiDrawHLine(gfx_vis, 0, gfx_ymax - (20 + gfx_ymax / 48 + 1), (gfx_xmax * 7) / 10 + gfx_xmax / 64); ggiDrawVLine(gfx_vis, (gfx_xmax * 7) / 10 + gfx_xmax / 64, 0, gfx_ymax); gfxDrawPlayerList(); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(0, 60 + (gfx_ymax / 4 - 20), "\020"); txtDrawListBox(wpnlist); gfxUpdate(); gfxDoUpdate(); while(gm_gamemode == PREGAME && !gm_quit) { tv.tv_sec = 0; tv.tv_usec = 5000; rlMain(relay, &tv); tv.tv_sec = 0; tv.tv_usec = 5000; if(ggiEventPoll (gfx_vis, emKeyPress | emKeyRepeat | emKeyRelease | emExpose, &tv)) { ggiEventRead(gfx_vis, &ev, emKeyPress | emKeyRepeat | emKeyRelease | emExpose); switch (ev.any.type) { case evKeyPress: case evKeyRepeat: switch (ev.key.sym) { case GIIUC_Return: if(ch_inpbox->string[0]) { rlSendTyped(relay, svrid, "MS", ch_inpbox->string, strlen(ch_inpbox->string) + 1); txtClearInputBox(ch_inpbox); gfxUpdate(); } break; case GIIUC_r: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { char buf[256]; struct ChangeReady_pkt cr; cr.type[0] = 'C'; cr.type[1] = 'R'; cr.id = gm_myid; cr.r = (ubyte_pkt)READY; rlSend(relay, svrid, buf, pktPackChangeReady(buf, &cr)); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); } break; case GIIUC_o: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { char buf[256]; struct ChangeReady_pkt cr; cr.type[0] = 'C'; cr.type[1] = 'R'; cr.id = gm_myid; cr.r = (ubyte_pkt)OBSERVER; rlSend(relay, svrid, buf, pktPackChangeReady(buf, &cr)); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); } break; case GIIUC_q: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { gm_quit = 1; } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); } break; case GIIUC_n: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { rlSendTyped(relay, svrid, "SN", ch_inpbox->string, strlen(ch_inpbox->string) + 1); txtClearInputBox(ch_inpbox); gfxUpdate(); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); } break; case GIIUC_h: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { ggiSetGCForeground(gfx_vis, gfx_white); txtScrollWindowPrintf(ch_scrlwin, "- KOTH Pregame keys -"); txtScrollWindowPrintf(ch_scrlwin, "Alt-r toggles readiness, Alt-n changes name"); txtScrollWindowPrintf(ch_scrlwin, "Alt-o toggles observer"); txtScrollWindowPrintf(ch_scrlwin, "Alt-q quits, Up/Down select weapon"); txtScrollWindowPrintf(ch_scrlwin, "Right buys, Left sells"); txtScrollWindowPrintf(ch_scrlwin, "- KOTH Ingame keys -"); txtScrollWindowPrintf(ch_scrlwin, "Right/Left, Change the angle of turret"); txtScrollWindowPrintf(ch_scrlwin, "Up/Down, Increase/Decrease power/velocity of shot"); txtScrollWindowPrintf(ch_scrlwin, "Space changes weapon, Enter fire"); txtScrollWindowPrintf(ch_scrlwin, "t talk, Alt-R updates terrain"); gfxUpdate(); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); } break; case GIIK_Up: case GIIK_Down: case GIIK_Right: case GIIK_Left: if (gm_myplstruct->ready == READY) { ggiSetGCForeground(gfx_vis, gfx_white); txtScrollWindowPrintf(ch_scrlwin, "Error: READY players can't touch the weapons"); txtScrollWindowPrintf(ch_scrlwin, "Press alt-r to toggle ready"); gfxUpdate(); break; } ggiSetGCForeground(gfx_vis, gfx_white); txtDoListBox(wpnlist, &ev); gfxUpdate(); break; default: ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_inpbox, &ev); gfxUpdate(); break; } break; case evExpose: gfxUpdate(); break; } } gfxDoUpdate(); } txtClearInputBox(ch_inpbox); } void clDrawStatus(statusUpdate update) { char buf[512]; char buf2[512]; int i; Player_pl *pcur; switch(update) { case SU_ALL: case SU_ANGLE: clEraseStatus(SU_ANGLE); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(ANGLE_VELO_X, ANGLE_VELO_Y, "%.0f/%.0f", (gm_myplstruct->fire_angle > 90) ? 90 - (gm_myplstruct->fire_angle - 90) : gm_myplstruct->fire_angle, gm_myplstruct->fire_velocity); if(update!=SU_ALL) break; /* else fall through */ case SU_ARMOR: clEraseStatus(SU_ARMOR); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(ARMOR_SHIELD_X, ARMOR_SHIELD_Y, "%i (%i)", gm_myplstruct->armor, gm_myplstruct->shield); if(update!=SU_ALL) break; /* else fall through */ case SU_WEAPON: clEraseStatus(SU_WEAPON); ggiSetGCForeground(gfx_vis, gfx_white); if(gm_curitem!=NULL) { switch(gm_curitem->type) { case WEAPON: txtPrintf(WEAPON_COUNT_X, WEAPON_COUNT_Y, "%s (%i)", ((Weapon_wep*)gm_curitem->info)->name, gm_curitem->count > 99 ? 99 : gm_curitem->count); break; case SHIELD: break; } } if(update!=SU_ALL) break; /* else fall through */ case SU_WIND: clEraseStatus(SU_WIND); ggiSetGCForeground(gfx_vis, gfx_white); balPrintWind(buf); if(last_wind!=bal_wind) { if(last_wind>0) sprintf(buf2, " /wind %i \020", last_wind); else if(last_wind<0) sprintf(buf2, " /\021 wind %i", -last_wind); else sprintf(buf2," /no wind"); strcat(buf, buf2); } txtPrintf(WIND_SPEED_X,WIND_SPEED_Y,buf); if(update!=SU_ALL) break; /* else fall through */ case SU_PLAYER: clEraseStatus(SU_PLAYER); /* if in simultaneous mode */ if(gm_gametype == SIMULTANEOUS) { i=0; for(pcur=pl_begin; pcur; pcur=pcur->next) { if((pcur->ready==READY) && (pcur->fired==0)) { logPrintf(DEBUG, "%s not fired yet\n", pcur->name); ggiSetGCForeground(gfx_vis, gfx_tankcolor[pcur->tankcolor]); ggiDrawVLine(gfx_vis, TURN_X+i++, TURN_Y, 20); ggiDrawVLine(gfx_vis, TURN_X+i++, TURN_Y, 20); ggiDrawVLine(gfx_vis, TURN_X+i++, TURN_Y, 20); i++; } } } /* else, if in turn mode */ else if(gm_gametype == TAKETURNS) { Player_pl *pl = plLookupPlayer(curShooterId); if(!pl) break; ggiSetGCForeground(gfx_vis, gfx_tankcolor[pl->tankcolor]); for(i=0; i<20; i++) { ggiDrawVLine(gfx_vis, TURN_X+i, TURN_Y, 20); } } if(update!=SU_ALL) break; /* else fall through */ case SU_ALLPLSTATUS: if(gm_statusArea == 1) { char tempstr[8]; int xpos; gfxUpdate(); gfxDoUpdate(); clEraseStatus(SU_ALLPLSTATUS); for(pcur=pl_begin; pcur; pcur=pcur->next) { if(pcur->ready==READY) { /* print the text in the same color as the tank */ ggiSetGCForeground(gfx_vis, gfx_tankcolor[pcur->tankcolor]); /* clip the name at 8 characters */ strncpy(tempstr, pcur->name, 8); tempstr[7]='\0'; /* figure out the x position... make sure it's not past ** the left screen edge */ xpos=gfxTerrainToScreenXCoord(pcur->x)-TANKSCREENRATIO_X; xpos=(xposarmor, pcur->shield); } } } if(update!=SU_ALL) break; /* else fall through */ default: ; } gfxUpdate(); } void clEraseStatus(statusUpdate update) { switch(update) { case SU_ALL: case SU_ANGLE: gfxDrawArea(gfxScreenToTerrainXCoord(ANGLE_VELO_X), gfxScreenToTerrainYCoord(ANGLE_VELO_Y+21), gfxScaleScreenToTerrainXDimen(ANGLE_VELO_W), gfxScaleScreenToTerrainYDimen(20)); if(update!=SU_ALL) break; /* else fall through */ case SU_ARMOR: gfxDrawArea(gfxScreenToTerrainXCoord(ARMOR_SHIELD_X), gfxScreenToTerrainYCoord(ARMOR_SHIELD_Y+21), gfxScaleScreenToTerrainXDimen(ARMOR_SHIELD_W), gfxScaleScreenToTerrainYDimen(20)); if(update!=SU_ALL) break; /* else fall through */ case SU_WEAPON: gfxDrawArea(gfxScreenToTerrainXCoord(WEAPON_COUNT_X), gfxScreenToTerrainYCoord(WEAPON_COUNT_Y+21), gfxScaleScreenToTerrainXDimen(WEAPON_COUNT_W), gfxScaleScreenToTerrainYDimen(20)); if(update!=SU_ALL) break; /* else fall through */ case SU_WIND: gfxDrawArea(gfxScreenToTerrainXCoord(WIND_SPEED_X), gfxScreenToTerrainYCoord(WIND_SPEED_Y+21), gfxScaleScreenToTerrainXDimen(WIND_SPEED_W), gfxScaleScreenToTerrainYDimen(20)); if(update!=SU_ALL) break; /* else fall through */ case SU_PLAYER: gfxDrawArea(gfxScreenToTerrainXCoord(TURN_X), gfxScreenToTerrainYCoord(TURN_Y+21), gfxScaleScreenToTerrainXDimen(TURN_W), gfxScaleScreenToTerrainYDimen(20)); if(update!=SU_ALL) break; /* else fall through */ case SU_ALLPLSTATUS: if(gm_statusArea == 1) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, ALLPLSTATUS_X, ALLPLSTATUS_Y, ALLPLSTATUS_W, ALLPLSTATUS_H); } if(update!=SU_ALL) break; /* else fall through */ default: ; } gfxUpdate(); } void clPlaygame() { struct timeval tv; struct timespec req; /* how much sleep each cycle */ ggi_event ev; int i, tl = 0, fl = 0, prvang; char typing_message = 0; Player_pl *pcur; struct Projectilelist_bal *prj; TerrainSpans_ter *tmp; int s, vm; char wind_already_processed = 0; req.tv_sec = 0; req.tv_nsec = 20000000; bal_wind=0; last_wind=0; for(gm_curitem = gm_myplstruct->itemstock; gm_curitem->count == 0; gm_curitem = gm_curitem->next) ; if(gm_chatlines > 0) txtClearScrollWindow(ch_ingmmsg); ggiSetGCForeground(gfx_vis, gfx_white); /*clDrawStatus(SU_ALL);*/ logPrintf(SPAM, "Booting playgame mainloop\n"); while(((gm_gamemode == INGAME) || (gm_gamemode == POSTGAME && gm_stuff_happening) || (bal_Projectiles != NULL)) && !gm_quit) { gm_stuff_happening = terCalcDirtFall(); if(gm_stuff_happening) { gfxUpdate(); for(i = 0, s = -1; i < ter_sizex; i++) { for(tmp = &(ter_data[i]), vm = 0; tmp; tmp = tmp->nexthigher) { if(tmp->vy != 0) { vm = 1; break; } } if(s == -1 && vm) s = i; if(vm == 0 && s != -1) { gfxDrawArea(s - gfxScaleScreenToTerrainXDimen(1), 0, i - s + gfxScaleScreenToTerrainXDimen(2), ter_sizey); s = -1; } } gm_ctf = 1; } if(gm_ctf && ch_gotallterrain != 0) { gfxUpdate(); gm_ctf = plCalcTankFall(); for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready == READY && (pcur->ox != pcur->x || pcur->oy != pcur->y)) gfxDrawTank(pcur); } if(!gm_stuff_happening) { gm_stuff_happening = gm_ctf; } } if(bal_Projectiles && ch_gotallterrain) { if(balAdvanceProjectiles()) { gfxUpdate(); gm_ctf = 1; gm_stuff_happening = gm_ctf; } } chProcessDeathQueue(); if(gm_stuff_happening || ch_gotallterrain == 1) { clDrawStatus(SU_ALL); gfxDoUpdate(); ch_gotallterrain = 2; } if(gm_stuff_happening == 0 && ch_gotallterrain) { logPrintf(SPAM, "gm_WS_pos=%i ; gm_AS_pos=%i ; wind_processed=%i\n", gm_WS_pos, gm_AS_pos, wind_already_processed); if(gm_AS_pos > 0 && wind_already_processed) { wind_already_processed = 0; gm_AS_pos--; for(prj = bal_Projectiles; prj; prj = prj->next) { if(prj->stat == HOLDING && prj->gen == gm_AS_queue[0]) { prj->stat = INITSHOT(prj); } } logPrintf(DEBUG, "-- Beginning round, gen: %i --\n", gm_AS_queue[0]); logPrintf(DEBUG, "Set wind to %i\n", bal_wind); for(i = 0; i < gm_AS_pos; i++) gm_AS_queue[i] = gm_AS_queue[i + 1]; } else if(gm_WS_pos > 0 && !wind_already_processed) { wind_already_processed = 1; gm_WS_pos--; last_wind=bal_wind; bal_wind = gm_WS_queue[0]; clDrawStatus(SU_WIND); for(i = 0; i < gm_WS_pos; i++) gm_WS_queue[i] = gm_WS_queue[i + 1]; } } if(!gm_stuff_happening) { tv.tv_sec = 0; tv.tv_usec = 8000; rlMain(relay, &tv); tv.tv_sec = 0; tv.tv_usec = 8000; } if(ggiEventPoll (gfx_vis, emKeyPress | emKeyRelease | emKeyRepeat | emExpose, &tv)) { ggiEventRead(gfx_vis, &ev, emKeyPress | emKeyRelease | emKeyRepeat | emExpose); switch (ev.any.type) { case evKeyPress: case evKeyRepeat: switch (ev.key.sym) { case GIIK_Left: if(ev.key.modifiers & GII_MOD_CTRL) tl = -5; else tl = -1; break; case GIIK_Right: if(ev.key.modifiers & GII_MOD_CTRL) tl = 5; else tl = 1; break; case GIIK_Up: if(ev.key.modifiers & GII_MOD_CTRL) fl = 20; else if(ev.key.modifiers & GII_MOD_SHIFT) fl = 1; else fl = 5; break; case GIIK_Down: if(ev.key.modifiers & GII_MOD_CTRL) fl = -20; else if(ev.key.modifiers & GII_MOD_SHIFT) fl = -1; else fl = -5; break; break; case GIIUC_Escape: if(typing_message) ch_ingameinpbox->string[0] = 0; else break; /* * Fall though */ case GIIUC_Return: if(typing_message) { if(ch_ingameinpbox->string[0]) { rlSendTyped (relay, svrid, "MS", ch_ingameinpbox->string, strlen(ch_ingameinpbox->string) + 1); } if(gm_chatlines > 0) { txtClearInputBox(ch_ingameinpbox); } else { ch_ingameinpbox->string[0] = 0; ch_ingameinpbox->curpos = 0; gfxDrawArea (gfxScreenToTerrainXCoord (ch_ingameinpbox-> x), gfxScreenToTerrainYCoord (ch_ingameinpbox-> y) - gfxScaleScreenToTerrainYDimen (ch_ingameinpbox-> h), gfxScaleScreenToTerrainXDimen (ch_ingameinpbox->w), gfxScaleScreenToTerrainYDimen (ch_ingameinpbox->h)); } typing_message = 0; gfxUpdate(); } else { gm_curitem->activate(gm_curitem->info); } break; case GIIK_PPlus: case GIIUC_Plus: case GIIUC_Space: if(!typing_message) { for(gm_curitem = gm_curitem->next; gm_curitem->count <= 0; gm_curitem = gm_curitem->next) ; clDrawStatus(SU_WEAPON); break; } /* * fall through */ case GIIK_PMinus: case GIIUC_Minus: if(!typing_message) { for(gm_curitem = gm_curitem->prev; gm_curitem->count <= 0; gm_curitem = gm_curitem->prev) ; clDrawStatus(SU_WEAPON); break; } /* * fall through */ case GIIUC_q: if(!typing_message && (ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META)) { gm_quit = 1; break; } /* * fall through */ case GIIUC_t: if(!typing_message) { typing_message = 1; txtClearInputBox(ch_ingameinpbox); gfxUpdate(); break; } /* * Fall through */ case GIIUC_r: if(!typing_message && (ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META)) { rlSend(relay, svrid, "RS", 2); ch_gotallterrain = 0; if(gm_stuff_happening) { /* * The server will sends the updated terrain with * the projectiles actions */ balClearAllProjectiles(); } break; } /* * Fall through */ /* * case GIIUC_r: if(!typing_message) { gfxDrawArea(0, * 0, ter_sizex, ter_sizey); gfxUpdate(); break; } */ /* * Fall through */ default: if(typing_message) { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_ingameinpbox, &ev); gfxUpdate(); } break; } break; case evKeyRelease: switch (ev.key.sym) { case GIIK_Left: case GIIK_Right: tl = 0; break; case GIIK_Up: case GIIK_Down: fl = 0; break; } break; case evExpose: gfxUpdate(); break; } } if(tl < 0 && gm_myplstruct->ready == READY) { if(gm_myplstruct->fire_angle < 180) gm_myplstruct->fire_angle -= tl; if(gm_myplstruct->fire_angle > 180) gm_myplstruct->fire_angle = 180; if(gm_myplstruct->fire_angle >= 90) gm_myplstruct->barreloff_x = gm_myplstruct->barreloff_left; gfxDrawTank(gm_myplstruct); } else if(tl > 0 && gm_myplstruct->ready == READY) { prvang = gm_myplstruct->fire_angle; if(gm_myplstruct->fire_angle > 0) gm_myplstruct->fire_angle -= tl; if(gm_myplstruct->fire_angle < 0) gm_myplstruct->fire_angle = 0; if(gm_myplstruct->fire_angle < 90) gm_myplstruct->barreloff_x = gm_myplstruct->barreloff_right; gfxDrawTank(gm_myplstruct); } if(fl > 0 && gm_myplstruct->fire_velocity < 1000 && gm_myplstruct->ready == READY) gm_myplstruct->fire_velocity += fl; else if(fl < 0 && gm_myplstruct->fire_velocity > 0 && gm_myplstruct->ready == READY) gm_myplstruct->fire_velocity += fl; if(gm_myplstruct->fire_velocity > 1000) gm_myplstruct->fire_velocity = 1000; if(gm_myplstruct->fire_velocity < 0) gm_myplstruct->fire_velocity = 0; if(tl || fl) { clDrawStatus(SU_ANGLE); } gfxDoUpdate(); if(gm_stuff_happening) nanosleep(&req, NULL); } } void chProcessDeathQueue() { unsigned i; Player_pl *pcur, *pcur2; Weapon_wep *wp; if(gm_dq_pos > 0) { gfxUpdate(); gfxDrawArea(gfxScreenToTerrainXCoord(ch_ingmmsg->x), gfxScreenToTerrainYCoord(ch_ingmmsg->y) - gfxScaleScreenToTerrainYDimen(ch_ingmmsg-> h), gfxScaleScreenToTerrainXDimen(ch_ingmmsg->w), gfxScaleScreenToTerrainYDimen(ch_ingmmsg->h)); ggiSetGCForeground(gfx_vis, gfx_red); if(gm_chatlines == 0) ch_ingmmsg->currow = 0; for(i = 0; i < gm_dq_pos; i += 3) { if(gm_death_queue[i + 1] == gm_death_queue[i]) { pcur = plLookupPlayer(gm_death_queue[i]); wp = wepLookupWeaponByID(gm_death_queue[i+2]); assert(wp); if(pcur) { txtScrollWindowPrintf(ch_ingmmsg, "%s aimed his own head with a %s.", pcur->name, wp->name); logPrintf(INTERESTING, "%s aimed his own head with a %s\n", pcur->name, wp->name); gfxEraseTank(pcur, 0); } } else if(gm_death_queue[i + 1] == 0) { pcur = plLookupPlayer(gm_death_queue[i]); if(pcur) { pcur2 = plLookupPlayer(gm_death_queue[i+2]); if(pcur2) { if(pcur2 != pcur) { txtScrollWindowPrintf(ch_ingmmsg, "%s was cratered by %s.", pcur->name, pcur2->name); logPrintf(INTERESTING, "%s was cratered by %s.\n", pcur->name, pcur2->name); } else { txtScrollWindowPrintf(ch_ingmmsg, "%s cratered himself.", pcur->name); logPrintf(INTERESTING, "%s cratered himself.\n", pcur->name); } } else { txtScrollWindowPrintf(ch_ingmmsg, "%s cratered.", pcur->name); logPrintf(INTERESTING, "%s cratered\n", pcur->name); } gfxEraseTank(pcur, 0); } } else { pcur = plLookupPlayer(gm_death_queue[i]); pcur2 = plLookupPlayer(gm_death_queue[i + 1]); wp = wepLookupWeaponByID(gm_death_queue[i+2]); assert(wp); if(pcur && pcur2) { txtScrollWindowPrintf(ch_ingmmsg, "%s blew %s to bits with a %s.", pcur2->name, pcur->name, wp->name); logPrintf(INTERESTING, "%s blew %s to bits with a %s.\n", pcur2->name, pcur->name, wp->name); gfxEraseTank(pcur, 0); } } } gm_dq_pos = 0; } } typedef struct { int id; int round; int total; } Scores; /* * qsort comparison function */ int scompar(const void *a, const void *b) { Scores *sa; Scores *sb; sa = (Scores *) a; sb = (Scores *) b; return (sb->total - sa->total); } /* * post game */ void clPostgame() { struct timeval tv; ggi_event ev; Player_pl *pcur; int y; char tmptxt[1000]; int i = 0; Scores scores[20] = { {0, 0, 0} }; ggi_color grey; /* Clear the queues */ gm_WS_pos = 0; gm_AS_pos = 0; gm_dq_pos = 0; /* * clear the scores struct */ for(i = 0; i < 20; i++) { scores[i].id = 0; scores[i].round = 0; scores[i].total = 0; } /* * clear the screen */ ggiSetGCForeground(gfx_vis, 0); ggiFillscreen(gfx_vis); /* * reset the y position */ y = 0; /* * put the scores up */ ggiSetGCForeground(gfx_vis, gfx_white); if(gm_currentRound >= gm_totalRounds) { sprintf(tmptxt, "Final Score"); } else { sprintf(tmptxt, "Score - Round %d of %d", gm_currentRound, gm_totalRounds); } txtPrintf(0, y, tmptxt); y += 40; sprintf(tmptxt, "%12s: %12s / %12s", " Player name", "round score", "total score"); txtPrintf(0, y, tmptxt); y += 20; i = 0; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) { /* load the score array */ scores[i].id = pcur->id; scores[i].total = pcur->score; scores[i].round = pcur->roundScore; i++; } } /* * sort the score array */ qsort(&scores, i, sizeof(scores[0]), scompar); /* * output the sorted scores */ for(i = 0; i < 20; i++) { if(scores[i].id != 0) { if(scores[i].id == gm_myid) { ggiSetGCForeground(gfx_vis, gfx_green); } else { ggiSetGCForeground(gfx_vis, gfx_white); } sprintf(tmptxt, "%12s: %12d / %12d", plLookupPlayer(scores[i].id)->name, scores[i].round, scores[i].total); txtPrintf(0, y, tmptxt); y += 20; } } ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(0, y, "Alt-r continues, Alt-q quits"); y += 20; txtPrintf(0, y, "otherwise, chatting"); y += 20; /* * draw dividing lines by the chat text */ grey.r = 0x8888; grey.g = 0x8888; grey.b = 0x8888; ggiSetGCForeground(gfx_vis, ggiMapColor(gfx_vis, &grey)); ggiDrawHLine(gfx_vis, 0, gfx_ymax - 25, gfx_xmax); ggiDrawHLine(gfx_vis, 0, gfx_ymax - 110, gfx_xmax); gfxUpdate(); gfxDoUpdate(); while(gm_gamemode == POSTGAME && !gm_quit) { tv.tv_sec = 0; tv.tv_usec = 20000; rlMain(relay, &tv); tv.tv_sec = 0; tv.tv_usec = 20000; if(ggiEventPoll(gfx_vis, emKeyPress | emExpose | emKeyRepeat, &tv)) { ggiEventRead(gfx_vis, &ev, emKeyPress | emExpose | emKeyRepeat); switch (ev.any.type) { case evKeyPress: case evKeyRepeat: switch (ev.key.sym) { case GIIUC_Return: if(ch_postinpbox->string[0]) { rlSendTyped(relay, svrid, "MS", ch_postinpbox->string, strlen(ch_postinpbox->string) + 1); txtClearInputBox(ch_postinpbox); gfxUpdate(); } break; case GIIUC_r: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { char buf[256]; struct ChangeReady_pkt cr; cr.type[0] = 'C'; cr.type[1] = 'R'; cr.id = gm_myid; cr.r = (ubyte_pkt)READY; rlSend(relay, svrid, buf, pktPackChangeReady(buf, &cr)); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_postinpbox, &ev); gfxUpdate(); } break; case GIIUC_o: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { char buf[256]; struct ChangeReady_pkt cr; cr.type[0] = 'C'; cr.type[1] = 'R'; cr.id = gm_myid; cr.r = (ubyte_pkt)OBSERVER; rlSend(relay, svrid, buf, pktPackChangeReady(buf, &cr)); } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_postinpbox, &ev); gfxUpdate(); } break; case GIIUC_q: if(ev.key.modifiers & GII_MOD_ALT || ev.key.modifiers & GII_MOD_META) { gm_quit = 1; } else { ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_postinpbox, &ev); gfxUpdate(); } break; default: ggiSetGCForeground(gfx_vis, gfx_white); txtDoInputBox(ch_postinpbox, &ev); gfxUpdate(); break; } break; case evExpose: gfxUpdate(); break; } } gfxDoUpdate(); } /* * clear the screen to be nice */ ggiSetGCForeground(gfx_vis, 0); ggiFillscreen(gfx_vis); txtClearInputBox(ch_postinpbox); } void clDriverloop() { txtScrollWindowPrintf(ch_scrlwin, "Getting startup information"); while(gm_gamemode == NOTPLAYING || gm_myid == -1) rlMain(relay, NULL); gm_myplstruct = plLookupPlayer(gm_myid); while(!gm_quit) { if(gm_gamemode == PREGAME) { ggiSetGCForeground(gfx_vis, gfx_white); txtScrollWindowPrintf(ch_scrlwin, "Currently in Pregame"); clPregame(); } if(gm_gamemode == INGAME && !gm_quit) { logPrintf(SPAM, "Currently in Game\n"); clPlaygame(); } if(gm_gamemode == POSTGAME && !gm_quit) { clPostgame(); } } } void clShutdown() { gfxShutdown(); } int main(int argc, char **argv) { clInitialize(argc, argv); clDriverloop(); clShutdown(); if(demo == 1 || observe == 1) { demoStopDemo(); } return 0; } /* * Some stub routines. */ void aihDamageReport(Player_pl * hit_pl, int srcid, int amt) { } void aihExplosionHook(Projectilepos_bal * prj) { }