diff options
Diffstat (limited to 'src/game.cpp')
-rw-r--r-- | src/game.cpp | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 00000000..ac90b56b --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,663 @@ +/** + + The Mana World + Copyright 2004 The Mana World Development Team + This file is part of The Mana World. + + The Mana World 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 + any later version. + + The Mana World 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 The Mana World; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "main.h" +#include "map.h" +#include "being.h" +#include "log.h" +#include "./Gui/chat.h" +#include "./Gui/gui.h" +#include "./Gui/inventory.h" +#include "./Graphic/SuperEagle.h" +#include "./Graphic/graphic.h" +#include "./Sound/sound.h" + +char map_path[480]; + +extern TmwSound sound; + +unsigned short dest_x, dest_y, src_x, src_y; +unsigned int player_x, player_y; +bool refresh_beings = false; +unsigned char keyb_state; +volatile int tick_time; +volatile bool refresh = false, action_time = false; + +#define MAX_TIME 10000 + +/** Finite states machine to keep track + of player walking status (2 steps linear + prediction) + 0 = Standing + 1 = Walking without confirm packet + 2 = Walking with confirm */ +char walk_status = 0; + + + +void refresh_time(void) { + tick_time++; + if(tick_time==MAX_TIME)tick_time = 0; +} +END_OF_FUNCTION(refresh_frame); + +void refresh_screen(void) { + refresh = true; +} +END_OF_FUNCTION(refresh_screen); + +int fps = 0, frame = 0; +/** lets u only trigger an action every other second + tmp. counts fps*/ +void second(void) { + action_time = true; + fps = (fps + frame)/2; + frame = 0; +} +END_OF_FUNCTION(second); + +/** Return elapsed time (Warning: very unsafe + function. It supposes the delay is always < 10s) */ +short get_elapsed_time(short start_time) { + if(start_time<=tick_time) + return tick_time-start_time; + else + return tick_time+(MAX_TIME-start_time); +} + +/** Main game loop */ +void game() { + status("INIT"); + do_init(); + init_graphic(); + while(state!=EXIT) { + status("INPUT"); + do_input(); + status("LOGIC"); + do_logic(); + if(refresh) { + status("GRAPHIC"); + do_graphic(); + refresh = false; + } + status("PARSE"); + do_parse(); + status("FLUSH"); + flush(); + } + + exit_graphic(); + close_session(); +} + +/** Initialize game engine */ +void do_init() { + + if(!load_map(map_path))error("Could not find map file"); + + sound.StartMOD("Sound/Mods/somemp.xm",-1); + + // Initialize timers + tick_time = 0; + refresh = false; + LOCK_VARIABLE(tick_time); + LOCK_VARIABLE(refresh); + install_int_ex(refresh_time, MSEC_TO_TIMER(1)); + install_int_ex(refresh_screen, /*MSEC_TO_TIMER(2000)*/BPS_TO_TIMER(75)); // Set max refresh rate to 75 fps + install_int_ex(second, BPS_TO_TIMER(1)); + + // Interrupt drawing while in background + #ifdef WIN32 + set_display_switch_mode(SWITCH_AMNESIA); + #else + set_display_switch_mode(SWITCH_PAUSE); + #endif + + // Initialize beings + empty(); + player_node = create_node(); + player_node->id = account_ID; + player_node->type = ACTION_NODE; + set_coordinates(player_node->coordinates, x, y, 0); + player_node->speed = 150; + add_node(player_node); + keyb_state = IDLE; + + remove("packet.list"); +} + +/** Clean the engine */ +void do_exit() { +} + +/** Check user input */ +void do_input() { + + if(walk_status==0) { + int x = get_x(player_node->coordinates); + int y = get_y(player_node->coordinates); + + if(key[KEY_UP]) { + if(get_walk(x, y-1)!=0) { + walk(x, y-1, NORTH); + walk_status = 1; + src_x = x; + src_y = y; + player_node->action = WALK; + player_node->tick_time = tick_time; + set_coordinates(player_node->coordinates, x, y-1, NORTH); + } else set_coordinates(player_node->coordinates, x, y, NORTH); + } else if(key[KEY_DOWN]) { + if(get_walk(x, y+1)!=0) { + walk(x, y+1, SOUTH); + walk_status = 1; + src_x = x; + src_y = y; + player_node->action = WALK; + player_node->tick_time = tick_time; + set_coordinates(player_node->coordinates, x, y+1, SOUTH); + } else set_coordinates(player_node->coordinates, x, y, SOUTH); + } else if(key[KEY_LEFT]) { + if(get_walk(x-1, y)!=0) { + walk(x-1, y, WEST); + walk_status = 1; + src_x = x; + src_y = y; + player_node->action = WALK; + player_node->tick_time = tick_time; + set_coordinates(player_node->coordinates, x-1, y, WEST); + } else set_coordinates(player_node->coordinates, x, y, WEST); + } else if(key[KEY_RIGHT]) { + if(get_walk(x+1, y)!=0) { + walk(x+1, y, EAST); + walk_status = 1; + src_x = x; + src_y = y; + player_node->action = WALK; + player_node->tick_time = tick_time; + set_coordinates(player_node->coordinates, x+1, y, EAST); + } else set_coordinates(player_node->coordinates, x, y, EAST); + } + } + + if(player_node->action==STAND) + if(key[KEY_LCONTROL]) { + player_node->action = ATTACK; + attack(x, y, get_direction(player_node->coordinates)); + player_node->tick_time = tick_time; + } + + if(key[KEY_F1]) { + save_bitmap("./Graphic/screenshot.bmp",double_buffer,NULL); + } else if(key[KEY_F12]){ + sound.SetAdjVol( 1, 1, 1); + } else if(key[KEY_F11]){ + sound.SetAdjVol(-1,-1,-1); + } + if(key[KEY_F5] && action_time==true) { + if(player_node->action==STAND) + action(2, 0); + else if(player_node->action==SIT) + action(3, 0); + action_time = false; + } + if(key[KEY_F6] && action_time==true) { + inventory.toggle(); + action_time = false; + } + + if(key[KEY_ENTER]) { + if(strcmp(speech, "")!=0) { + chatlog.chat_send(char_info[0].name, speech); + strcpy(speech,""); + } + } + + if(mouse_b&2) { + if(!show_npc_dialog) { + int npc_x = mouse_x/32+map_x/32; + int npc_y = mouse_y/32+map_y/32; + int id = find_npc(npc_x+1, npc_y+1); + if(id!=0) { + WFIFOW(0) = net_w_value(0x0090); + WFIFOL(2) = net_l_value(id); + WFIFOB(6) = 0; + WFIFOSET(7); + } + } + } + + // Emotions, Skill dialog + if(key_shifts & KB_ALT_FLAG && action_time == true) { + if(player_node->emotion==0) { + unsigned char emotion = 0; + if(key[KEY_1]) + emotion = 1; + else if(key[KEY_2]) + emotion = 2; + else if(key[KEY_3]) + emotion = 3; + else if(key[KEY_4]) + emotion = 4; + else if(key[KEY_5]) + emotion = 5; + else if(key[KEY_6]) + emotion = 6; + else if(key[KEY_7]) + emotion = 7; + else if(key[KEY_8]) + emotion = 8; + else if(key[KEY_9]) + emotion = 9; + else if(key[KEY_0]) + emotion = 10; + if(emotion!=0) { + WFIFOW(0) = net_w_value(0x00bf); + WFIFOB(2) = emotion; + WFIFOSET(3); + action_time = false; + } + } + + if(key[KEY_S]) { + show_skill_dialog = !show_skill_dialog; + action_time = false; + } + } + + if(key[KEY_ESC])state = EXIT; + +} + + + +/** Update game logic */ +void do_logic() { +} + +/** Calculate packet length */ +int get_packet_length(short id) { + int len = get_length(id); + if(len==-1)len = RFIFOW(2); + return len; +} + +/** Parse data received from map server into input buffer */ +void do_parse() { + unsigned short id; + char *temp; + char direction; + NODE *node = NULL; + int len; + + + // We need at least 2 bytes to identify a packet + if(in_size>=2) { + // Check if the received packet is complete + while(in_size>=(len = get_packet_length(id = RFIFOW(0)))) { + // Add infos to log file and dump the latest received packet + char pkt_nfo[60]; + sprintf(pkt_nfo,"In-buffer size: %i Packet id: %x Packet length: %i",in_size,RFIFOW(0),len); + /*log_hex("Packet", "Packet_ID", RFIFOW(0)); + log_int("Packet", "Packet_length", get_length(RFIFOW(0))); + log_int("Packet", "Packet_in_size", RFIFOW(2)); + log_int("Packet", "In_size", in_size); + FILE *file = fopen("packet.dump", "wb"); + for(int i=0;i<len;i++) { + fprintf(file, "%x|%i|%c ", RFIFOB(i), RFIFOB(i), RFIFOB(i)); + if((i+1)%10==0)fprintf(file, "\n"); + } + fclose(file); + file = fopen("packet.list", "ab"); + fprintf(file, "%x\n", RFIFOW(0)); + fclose(file);*/ + + // Parse packet based on their id + switch(id) { + // Received speech + case 0x008d: + temp = (char *)malloc(RFIFOW(2)-8); + memset(temp, '\0', RFIFOW(2)-8); + memcpy(temp, RFIFOP(8), RFIFOW(2)-8); + node = find_node(RFIFOL(4)); + if(node!=NULL) { + if(node->speech!=NULL) { + free(node->speech); + node->speech = NULL; + node->speech_time = 0; + } + node->speech = temp; + node->speech_time = SPEECH_TIME; + node->speech_color = makecol(255, 255, 255); + chatlog.chat_log(node->speech, BY_OTHER, gui_font); + } + break; + case 0x008e: + case 0x009a: + temp = (char *)malloc(RFIFOW(2)-4); + memset(temp, '\0', RFIFOW(2)-4); + memcpy(temp, RFIFOP(4), RFIFOW(2)-4); + if(player_node->speech!=NULL) { + free(player_node->speech); + player_node->speech = NULL; + node->speech_time = 0; + } + player_node->speech = temp; + player_node->speech_time = SPEECH_TIME; + player_node->speech_color = makecol(255, 255, 255); + if(id==0x008e) + chatlog.chat_log(player_node->speech, BY_PLAYER, gui_font); + else + chatlog.chat_log(player_node->speech, BY_GM, gui_font); + break; + // Success to walk request + case 0x0087: + /*if(walk_status==1) { + if(get_dest_x(RFIFOP(6))==get_x(player_node->coordinates) && get_dest_y(RFIFOP(6))==get_y(player_node->coordinates))*/ + if(walk_status==1) + walk_status = 2; + /*else { + walk_status = 0; + set_coordinates(player_node->coordinates, src_x, src_y, get_direction(player_node->coordinates)); + player_node->action = STAND; + } + }*/ + break; + // Add new being + case 0x0078: + if(find_node(RFIFOL(2))==NULL) { + node = create_node(); + node->id = RFIFOL(2); + node->speed = RFIFOW(6); + if(node->speed==0)node->speed = 150; // Else division by 0 when calculating frame + node->job = RFIFOW(14); + memcpy(node->coordinates, RFIFOP(46), 3); + add_node(node); + } + break; + // Remove a being + case 0x0080: + node = find_node(RFIFOL(2)); + if(node!=NULL) { + if(RFIFOB(6)==1) { // Death + if(node->job>110) { + node->action = MONSTER_DEAD; + node->frame = 0; + node->tick_time = tick_time; + } else remove_node(RFIFOL(2)); + } else remove_node(RFIFOL(2)); + } + break; + // Player moving + case 0x01d8: + case 0x01d9: + node = find_node(RFIFOL(2)); + if(node==NULL) { + node = create_node(); + node->id = RFIFOL(2); + node->job = RFIFOW(16); + memcpy(node->coordinates, RFIFOP(46), 3); + add_node(node); + node->tick_time = tick_time; + node->speed = RFIFOW(6); + } + break; + // Monster moving + case 0x007b: + node = find_node(RFIFOL(2)); + if(node==NULL) { + node = create_node(); + node->action = STAND; + set_coordinates(node->coordinates, get_src_x(RFIFOP(50)), get_src_y(RFIFOP(50)), 0); + node->id = RFIFOL(2); + node->speed = RFIFOW(6); + node->job = RFIFOW(14); + add_node(node); + } + node->path = calculate_path(get_src_x(RFIFOP(50)),get_src_y(RFIFOP(50)),get_dest_x(RFIFOP(50)),get_dest_y(RFIFOP(50))); + if(node->path!=NULL) { + set_coordinates(node->coordinates, node->path->x, node->path->y, 0); + node->action = WALK; + node->tick_time = tick_time; + } + break; + // Being moving + case 0x01da: + node = find_node(RFIFOL(2)); + if(node==NULL) { + node = create_node(); + node->id = RFIFOL(2); + node->job = RFIFOW(14); + set_coordinates(node->coordinates, get_src_x(RFIFOP(50)), get_src_y(RFIFOP(50)), 0); + add_node(node); + } + if(node->action!=WALK) { + direction = get_direction(node->coordinates); + node->action = WALK; + if(get_dest_x(RFIFOP(50))>get_x(node->coordinates))direction = EAST; + else if(get_dest_x(RFIFOP(50))<get_x(node->coordinates))direction = WEST; + else if(get_dest_y(RFIFOP(50))>get_y(node->coordinates))direction = SOUTH; + else if(get_dest_y(RFIFOP(50))<get_y(node->coordinates))direction = NORTH; + else node->action = STAND; + if(node->action==WALK)node->tick_time = tick_time; + set_coordinates(node->coordinates, get_dest_x(RFIFOP(50)), get_dest_y(RFIFOP(50)), direction); + } + break; + // NPC dialog + case 0x00b4: + if(!strstr(npc_text, RFIFOP(8))) { + strcat(npc_text, RFIFOP(8)); + strcat(npc_text, "\n"); + show_npc_dialog = 1; + } + break; + // Warp + case 0x0091: + memset(map_path, '\0', 480); + append_filename(map_path, "./data/map/", RFIFOP(2), 480); + if(load_map(map_path)) { + empty(); + player_node = create_node(); + player_node->job = 0; + player_node->action = STAND; + player_node->frame = 0; + player_node->type = ACTION_NODE; + player_node->speed = 150; + player_node->id = account_ID; + x = RFIFOW(18); + y = RFIFOW(20); + set_coordinates(player_node->coordinates, x, y, 0); + add_node(player_node); + walk_status = 0; + // Send "map loaded" + WFIFOW(0) = net_w_value(0x007d); + WFIFOSET(2); + while(out_size>0)flush(); + } else error("Could not find map file"); + break; + // Skill ... + case 0x011a: + break; + case 0x01a4: + break; + // Action failed (ex. sit because you have not reached the right level) + case 0x0110: + CHATSKILL action; + + action.skill = RFIFOW(2); + action.bskill = RFIFOW(4); + action.unused = RFIFOW(6); + action.success = RFIFOB(8); + action.reason = RFIFOB(9); + + if(action.success != SKILL_FAILED && + action.bskill == BSKILL_EMOTE ) { + printf("Action: %d/%d", action.bskill, action.success); + } + chatlog.chat_log(action, gui_font); + break; + // Update stat values + case 0x00b0: + switch(RFIFOW(2)) { + case 5: + char_info->hp = RFIFOW(4); + break; + case 6: + char_info->max_hp = RFIFOW(4); + break; + case 7: + char_info->sp = RFIFOW(4); + break; + case 8: + char_info->max_sp = RFIFOW(4); + break; + case 11: + char_info->lv = RFIFOW(4); + break; + } + if(char_info->hp==0) { + ok("Message", "You're now dead, press ok to restart"); + WFIFOW(0) = net_w_value(0x00b2); + WFIFOB(2) = 0; + WFIFOSET(3); + } + break; + // Stop walking + /*case 0x0088: // Disabled because giving some problems + if(node = find_node(RFIFOL(2))) { + if(node->id!=player_node->id) { + node->action = STAND; + node->frame = 0; + set_coordinates(node->coordinates, RFIFOW(6), RFIFOW(8), get_direction(node->coordinates)); + } + } + break;*/ + // Damage, sit, stand up + case 0x008a: + switch(RFIFOB(26)) { + case 0: // Damage + node = find_node(RFIFOL(6)); + if(node!=NULL) { + if(node->speech!=NULL) { + free(node->speech); + node->speech = NULL; + node->speech_time = SPEECH_TIME; + } + node->speech = (char *)malloc(5); + memset(node->speech, '\0', 5); + if(RFIFOW(22)==0) { + sprintf(node->speech, "miss"); + node->speech_color = makecol(255, 255, 0); + } else { + sprintf(node->speech, "%i", RFIFOW(22)); + if(node->id!=player_node->id)node->speech_color = makecol(0, 0, 255); + else node->speech_color = makecol(255, 0, 0); + } + node->speech_time = SPEECH_TIME; + if(RFIFOL(2)!=player_node->id) { + node = find_node(RFIFOL(2)); + if(node!=NULL) { + if(node->job<10) + node->action = ATTACK; + else node->action = MONSTER_ATTACK; + node->frame = 0; + } + } + } + break; + case 2: // Sit + case 3: // Stand up + node = find_node(RFIFOL(2)); + if(node!=NULL) { + if(RFIFOB(26)==2) + node->action = SIT; + else if(RFIFOB(26)==3) + node->action = STAND; + } + break; + } + break; + // Status change + case 0x00b1: + switch(RFIFOW(2)) { + case 1: + char_info->xp = RFIFOL(4); + break; + case 2: + char_info->job_xp = RFIFOL(4); + break; + case 14: + char_info->zeny = RFIFOL(4); + break; + // case 16 and 17 missing + } + break; + // Level up + case 0x019b: + if(RFIFOW(2)==0) { + ok("Info", "Level up!"); + } else if(RFIFOW(2)==1) { + ok("Info", "Job level up!"); + } + break; + // Emotion + case 0x00c0: + node = find_node(RFIFOL(2)); + if(node) { + node->emotion = RFIFOB(6); + node->emotion_time = EMOTION_TIME; + } + break; + // Update skill values + case 0x0141: + switch(RFIFOL(2)) { + case 0x000d: + char_info->STR = RFIFOL(6) + RFIFOL(10); // Base + Bonus + break; + case 0x000e: + char_info->AGI = RFIFOL(6) + RFIFOL(10); + break; + case 0x000f: + char_info->VIT = RFIFOL(6) + RFIFOL(10); + break; + case 0x0010: + char_info->INT = RFIFOL(6) + RFIFOL(10); + break; + case 0x0011: + char_info->DEX = RFIFOL(6) + RFIFOL(10); + break; + case 0x0012: + char_info->LUK = RFIFOL(6) + RFIFOL(10); + break; + } + break; + // Manage non implemented packets + default: + //alert(pkt_nfo,"","","","",0,0); + break; + } + //alert(pkt_nfo,"","","","",0,0); + + RFIFOSKIP(len); + } + } +} |