diff options
author | Eugenio Favalli <elvenprogrammer@gmail.com> | 2004-09-26 13:08:46 +0000 |
---|---|---|
committer | Eugenio Favalli <elvenprogrammer@gmail.com> | 2004-09-26 13:08:46 +0000 |
commit | 92bbeab96bf61edf9b7caa125ed67e634258383e (patch) | |
tree | a95dd426590c8e6208445290fa8b9b47c1a57bcb | |
parent | e46b2cdbf205d3d2e17266e3168fdbecd5f53222 (diff) | |
download | mana-92bbeab96bf61edf9b7caa125ed67e634258383e.tar.gz mana-92bbeab96bf61edf9b7caa125ed67e634258383e.tar.bz2 mana-92bbeab96bf61edf9b7caa125ed67e634258383e.tar.xz mana-92bbeab96bf61edf9b7caa125ed67e634258383e.zip |
*** empty log message ***
40 files changed, 7027 insertions, 0 deletions
diff --git a/ManaWorld.vcproj b/ManaWorld.vcproj index effc0451..75ff47d1 100644 --- a/ManaWorld.vcproj +++ b/ManaWorld.vcproj @@ -84,6 +84,7 @@ OutputFile="The Mana World.exe" LinkIncremental="1" GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb" SubSystem="2" OptimizeReferences="2" EnableCOMDATFolding="2" diff --git a/src/astar.cpp b/src/astar.cpp new file mode 100644 index 00000000..5fff9687 --- /dev/null +++ b/src/astar.cpp @@ -0,0 +1,326 @@ +#include "astar.h" +#include "being.h" + +const int numberPeople = 1; + int onClosedList = 10; + const int notfinished = 0;// path-related constants + + //Create needed arrays + //char get_path_walk [MAP_WIDTH][MAP_HEIGHT]; + int openList[MAP_WIDTH*MAP_HEIGHT+2]; //1 dimensional array holding ID# of open list items + int whichList[MAP_WIDTH+1][MAP_HEIGHT+1]; //2 dimensional array used to record +// whether a cell is on the open list or on the closed list. + int openX[MAP_WIDTH*MAP_HEIGHT+2]; //1d array stores the x location of an item on the open list + int openY[MAP_WIDTH*MAP_HEIGHT+2]; //1d array stores the y location of an item on the open list + int parentX[MAP_WIDTH+1][MAP_HEIGHT+1]; //2d array to store parent of each cell (x) + int parentY[MAP_WIDTH+1][MAP_HEIGHT+1]; //2d array to store parent of each cell (y) + int F_cost[MAP_WIDTH*MAP_HEIGHT+2]; //1d array to store F cost of a cell on the open list + int G_cost[MAP_WIDTH+1][MAP_HEIGHT+1]; //2d array to store G_cost cost for each cell. + int H_cost[MAP_WIDTH*MAP_HEIGHT+2]; //1d array to store H cost of a cell on the open list + int pathLength; //stores length of the FOUND path for critter + int pathLocation; //stores current position along the chosen path for critter + int* path_bank ; + + //Path reading variables + int pathStatus; + int xPath; + int yPath; + +/** Initialize pathfinder */ +void pathfinder_init() { + path_bank = (int*)malloc(4); +} + +/** Exit pathfinder */ +void pathfinder_exit() { + free(path_bank); +} + +/** Find path */ +PATH_NODE *find_path(int pathfinderID, int s_x, int s_y, int e_x, int e_y) { + int onOpenList=0, parentXval=0, parentYval=0, + a=0, b=0, m=0, u=0, v=0, temp=0, corner=0, numberOfOpenListItems=0, + addedGCost=0, tempG = 0, path = 0, x=0, y=0, + tempx, pathX, pathY, cellPosition, + newOpenListItemID=0; + + // If starting location and target are in the same location... + if (s_x==e_x && s_y==e_y && pathLocation>0)return NULL; + else if (s_x==e_x && s_y==e_y && pathLocation==0)return NULL; + + // If dest tile is NOT_WALKABLE, return that it's a NOT_FOUND path. + if(get_path_walk(e_x, e_y)==NOT_WALKABLE) { + xPath = s_x; + yPath = s_y; + return NULL; + } + + // Reset some variables that need to be cleared + for(x=0;x<MAP_WIDTH;x++) { + for(y=0;y<MAP_HEIGHT;y++) + whichList [x][y] = 0; + } + onClosedList = 2; //changing the values of onOpenList and onClosed list is faster than redimming whichList() array + onOpenList = 1; + pathLength = NOT_STARTED; + pathLocation = NOT_STARTED; + G_cost[s_x][s_y] = 0; //reset starting square's G_cost value to 0 + + // Add the starting location to the open list of tiles to be checked. + numberOfOpenListItems = 1; + openList[1] = 1;//assign it as the top (and currently only) item in the open list, which is maintained as a binary heap (explained below) + openX[1] = s_x ; openY[1] = s_y; + + // Do the following until a path is FOUND or deemed NOT_FOUND. + do { + + // If the open list is not empty, take the first cell off of the list. + // This is the lowest F cost cell on the open list. + if (numberOfOpenListItems != 0) { + + // Pop the first item off the open list. + parentXval = openX[openList[1]]; + parentYval = openY[openList[1]]; //record cell coordinates of the item + whichList[parentXval][parentYval] = onClosedList;//add the item to the closed list + + // Open List = Binary Heap: Delete this item from the open list, which + numberOfOpenListItems = numberOfOpenListItems - 1;//reduce number of open list items by 1 + + // Delete the top item in binary heap and reorder the heap, with the lowest F cost item rising to the top. + openList[1] = openList[numberOfOpenListItems+1];//move the last item in the heap up to slot #1 + v = 1; + + // Repeat the following until the new item in slot #1 sinks to its proper spot in the heap. + do { + u = v; + if (2*u+1 <= numberOfOpenListItems) { //if both children exist + //Check if the F cost of the parent is greater than each child. + //Select the lowest of the two children. + if(F_cost[openList[u]] >= F_cost[openList[2*u]])v = 2*u; + if(F_cost[openList[v]] >= F_cost[openList[2*u+1]])v = 2*u+1; + } else { + if (2*u <= numberOfOpenListItems) { //if only child #1 exists + //Check if the F cost of the parent is greater than child #1 + if (F_cost[openList[u]] >= F_cost[openList[2*u]])v = 2*u; + } + } + + if (u!=v) { // if parent's F is > one of its children, swap them + temp = openList[u]; + openList[u] = openList[v]; + openList[v] = temp; + } else break; //otherwise, exit loop + } while (u!=v); //reorder the binary heap + + +// Check the adjacent squares. (Its "children" -- these path children +// are similar, conceptually, to the binary heap children mentioned +// above, but don't confuse them. They are different. Path children +// are portrayed in Demo 1 with grey pointers pointing toward +// their parents.) Add these adjacent child squares to the open list +// for later consideration if appropriate (see various if statements +// below). + + for(b=parentYval-1;b<=parentYval+1;b++) { + for(a=parentXval-1;a<=parentXval+1;a++) { + // If not off the map (do this first to avoid array out-of-bounds errors) + if(a!=-1 && b!=-1 && a!=MAP_WIDTH && b!=MAP_HEIGHT) { + // If not already on the closed list (items on the closed list have + // already been considered and can now be ignored). + if(whichList[a][b]!=onClosedList) { + // If not a wall/obstacle square. + if (get_path_walk(a, b)!=NOT_WALKABLE) { + // Don't cut across corners + corner = WALKABLE; + if(a==parentXval-1) { + if(b==parentYval-1) { + if(get_path_walk(parentXval-1, parentYval)==NOT_WALKABLE || get_path_walk(parentXval, parentYval-1)==NOT_WALKABLE) // cera slash + corner = NOT_WALKABLE; + } else if (b==parentYval+1) { + if(get_path_walk(parentXval, parentYval+1)==NOT_WALKABLE || get_path_walk(parentXval-1, parentYval)==NOT_WALKABLE) + corner = NOT_WALKABLE; + } + } else if(a==parentXval+1) { + if(b==parentYval-1) { + if(get_path_walk(parentXval, parentYval-1)==NOT_WALKABLE || get_path_walk(parentXval+1, parentYval)==NOT_WALKABLE) + corner = NOT_WALKABLE; + } else if(b==parentYval+1) { + if(get_path_walk(parentXval+1, parentYval)==NOT_WALKABLE || get_path_walk(parentXval, parentYval+1)==NOT_WALKABLE) + corner = NOT_WALKABLE; + } + } + + if(corner==WALKABLE) { + // If not already on the open list, add it to the open list. + if (whichList[a][b]!=onOpenList) { + // Create a new open list item in the binary heap. + newOpenListItemID = newOpenListItemID + 1; //each new item has a unique ID # + m = numberOfOpenListItems+1; + openList[m] = newOpenListItemID;//place the new open list item (actually, its ID#) at the bottom of the heap + openX[newOpenListItemID] = a; + openY[newOpenListItemID] = b;//record the x and y coordinates of the new item + + //Figure out its G_cost cost + if (abs(a-parentXval) == 1 && abs(b-parentYval) == 1)addedGCost = 14;//cost of going to diagonal squares + else addedGCost = 10;//cost of going to non-diagonal squares + G_cost[a][b] = G_cost[parentXval][parentYval] + addedGCost; + + //Figure out its H and F costs and parent + H_cost[openList[m]] = 10*(abs(a - e_x) + abs(b - e_y)); + F_cost[openList[m]] = G_cost[a][b] + H_cost[openList[m]]; + parentX[a][b] = parentXval ; parentY[a][b] = parentYval; + + //Move the new open list item to the proper place in the binary heap. + //Starting at the bottom, successively compare to parent items, + //swapping as needed until the item finds its place in the heap + //or bubbles all the way to the top (if it has the lowest F cost). + while(m!=1) { // While item hasn't bubbled to the top (m=1) + //Check if child's F cost is < parent's F cost. If so, swap them. + if(F_cost[openList[m]]<=F_cost[openList[m/2]]) { + temp = openList[m/2]; + openList[m/2] = openList[m]; + openList[m] = temp; + m = m/2; + } else break; + } + + numberOfOpenListItems = numberOfOpenListItems+1;//add one to the number of items in the heap + //Change whichList to show that the new item is on the open list. + whichList[a][b] = onOpenList; + } else { // If whichList(a,b) = onOpenList + // If adjacent cell is already on the open list, check to see if this + // path to that cell from the starting location is a better one. + // If so, change the parent of the cell and its G_cost and F costs. + //Figure out the G_cost cost of this possible new path + if(abs(a-parentXval)==1 && abs(b-parentYval)==1)addedGCost = 14;//cost of going to diagonal tiles + else addedGCost = 10;//cost of going to non-diagonal tiles + + tempG = G_cost[parentXval][parentYval] + addedGCost; + + // If this path is shorter (G_cost cost is lower) then change + // the parent cell, G_cost cost and F cost. + if(tempG<G_cost[a][b]) { //if G_cost cost is less, + parentX[a][b] = parentXval; //change the square's parent + parentY[a][b] = parentYval; + G_cost[a][b] = tempG;//change the G_cost cost + + // Because changing the G_cost cost also changes the F cost, if + // the item is on the open list we need to change the item's + // recorded F cost and its position on the open list to make + // sure that we maintain a properly ordered open list. + + for(int x=1;x<=numberOfOpenListItems;x++) { //look for the item in the heap + if(openX[openList[x]]==a && openY[openList[x]]==b) { //item FOUND + F_cost[openList[x]] = G_cost[a][b] + H_cost[openList[x]];//change the F cost + //See if changing the F score bubbles the item up from it's current location in the heap + m = x; + while(m!=1) { //While item hasn't bubbled to the top (m=1) + //Check if child is < parent. If so, swap them. + if(F_cost[openList[m]]<F_cost[openList[m/2]]) { + temp = openList[m/2]; + openList[m/2] = openList[m]; + openList[m] = temp; + m = m/2; + } else break; + } + break; //exit for x = loop + } // If openX(openList(x)) = a + } // For x = 1 To numberOfOpenListItems + } // If tempG < G_cost(a,b) + } // else If whichList(a,b) = onOpenList + } // If not cutting a corner + } // If not a wall/obstacle square. + } // If not already on the closed list + } // If not off the map + } // for (a = parentXval-1; a <= parentXval+1; a++){ + } // for (b = parentYval-1; b <= parentYval+1; b++){ + } else {// if (numberOfOpenListItems != 0) + // If open list is empty then there is no path. + path = NOT_FOUND; + break; + } + //If target is added to open list then path has been FOUND. + if (whichList[e_x][e_y]==onOpenList) { + path = FOUND; + break; + } + + } while (path!=FOUND && path!=NOT_FOUND);//Do until path is FOUND or deemed NOT_FOUND + + // Save the path if it exists. + if (path == FOUND) { + + // Working backwards from the target to the starting location by checking + // each cell's parent, figure out the length of the path. + pathX = e_x; pathY = e_y; + do { + //Look up the parent of the current cell. + tempx = parentX[pathX][pathY]; + pathY = parentY[pathX][pathY]; + pathX = tempx; + + //Figure out the path length + pathLength = pathLength + 1; + } while (pathX != s_x || pathY != s_y); + + // Resize the data bank to the right size in bytes + path_bank = (int*) realloc (path_bank, pathLength*8); + + // Now copy the path information over to the databank. Since we are + // working backwards from the target to the start location, we copy + // the information to the data bank in reverse order. The result is + // a properly ordered set of path data, from the first step to the last. + pathX = e_x ; pathY = e_y; + cellPosition = pathLength*2;//start at the end + do { + cellPosition = cellPosition - 2;//work backwards 2 integers + path_bank [cellPosition] = pathX; + path_bank [cellPosition+1] = pathY; + // Look up the parent of the current cell. + tempx = parentX[pathX][pathY]; + pathY = parentY[pathX][pathY]; + pathX = tempx; + // If we have reached the starting square, exit the loop. + } while(pathX!=s_x || pathY!=s_y); + + char stringa[80]; + sprintf(stringa,"%i %i",s_x,s_y); + + PATH_NODE *ret = NULL, *temp = NULL; + pathLocation = 1; + ret = create_path_node(s_x,s_y); + temp = ret; + //alert(stringa,"","","","",0,0); + while(pathLocation<pathLength) { + sprintf(stringa,"%i %i",path_bank[pathLocation*2-2], path_bank[pathLocation*2-1]); + //alert(stringa,"","","","",0,0); + temp->next = create_path_node(path_bank[pathLocation*2-2], path_bank[pathLocation*2-1]); + if(temp->next==NULL)ok("Error", "Unable to create path node"); + temp = temp->next; + pathLocation++; + } + if(temp!=NULL)temp->next = create_path_node(e_x, e_y); + else ok("Error", "Null reference"); + return ret; + + } + return NULL; // Path not found +} + + + +/** Read the path data */ +void ReadPath(int pathfinderID) { + //If a path exists, read the path data + // from the pathbank. + pathLocation = 1; //set pathLocation to 1st step + while (pathLocation<pathLength) { + int a = path_bank [pathLocation*2-2]; + int b = path_bank [pathLocation*2-1]; + pathLocation = pathLocation + 1; + whichList[a][b] = 3;//draw dotted path + } +} + + + diff --git a/src/astar.h b/src/astar.h new file mode 100644 index 00000000..4a9fa045 --- /dev/null +++ b/src/astar.h @@ -0,0 +1,25 @@ +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#include <allegro.h> + +#include "map.h" +#include "being.h" + +#define NOT_STARTED 0 +#define FOUND 1 +#define NOT_FOUND 2 + +extern char walkability [MAP_WIDTH][MAP_HEIGHT]; +extern int whichList[MAP_WIDTH+1][MAP_HEIGHT+1]; +extern int G_cost[MAP_WIDTH+1][MAP_HEIGHT+1]; + +void ReadPath(int pathfinderID,int currentX,int currentY, int pixelsPerFrame); +int ReadPathX(int pathfinderID,int pathLocation); +int ReadPathY(int pathfinderID,int pathLocation); +void RenderScreen (bool stepByStep=false); +PATH_NODE *find_path(int pathfinderID,int startingX, int startingY, int targetX, int targetY); +void ReadPath(int pathfinderID); + + diff --git a/src/being.cpp b/src/being.cpp new file mode 100644 index 00000000..ca527882 --- /dev/null +++ b/src/being.cpp @@ -0,0 +1,210 @@ +/** + + 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 <stdio.h> +#include <memory.h> +#ifndef MACOSX + #include <malloc.h> +#endif + +#ifdef LINUXPPC + #include <malloc.h> +#endif + +#include "astar.h" +#include "being.h" + +NODE *player_node = NULL; +NODE *head = NULL; // First node of the list +unsigned int count = 0; // Number of beings in the list + +/** Create a path node */ +PATH_NODE *create_path_node(unsigned short x, unsigned short y) { + PATH_NODE *ret = (PATH_NODE *)malloc(sizeof(PATH_NODE)); + ret->x = x; + ret->y = y; + ret->next = NULL; + return ret; +} + +/** Return a* path */ +PATH_NODE *calculate_path(unsigned short src_x, unsigned short src_y, unsigned short dest_x, unsigned short dest_y) { + return find_path(1, src_x, src_y, dest_x, dest_y); +} + +/** Returns the first node of the list */ +NODE *get_head() { + return head; +} + +/** Creates a empty node */ +NODE *create_node() { + NODE *node = (NODE *)malloc(sizeof(NODE)); + node->id = 0; + node->job = 0; + memset(node->coordinates, 0, 3); + node->next = NULL; + node->action = 0; + node->frame = 0; + node->path = NULL; + node->speech = NULL; + node->speech_time = 0; + node->speech_color = makecol(255,255,255); + node->tick_time = 0; + node->speed = 150; + node->emotion = 0; + node->emotion_time = 0; + node->text_x = node->text_y = 0; + return node; +} + +/** Returns number of beings in the list */ +unsigned int get_count() { + return count; +} + +/** Removes all beings from the list */ +void empty() { + NODE *node = head; + NODE *next; + while(node) { + next = node->next; + free(node); + node = next; + } + count = 0; + head = NULL; +} + +/** Add a node to the list */ +void add_node(NODE *node) { + NODE *temp = head; + if(temp) { + while(temp->next) + temp = temp->next; + temp->next = node; + } else head = node; + count++; +} + +/** Remove a node */ +void remove_node(unsigned int id) { + unsigned int temp; + NODE *node_old, *node_new; + node_old = head; + node_new = NULL; + while(node_old) { + temp = get_id(node_old); + if(temp==id) { + if(node_new==NULL) { + head = node_old->next; + free(node_old); + count--; + return; + } else { + node_new->next = node_old->next; + free(node_old); + count--; + return; + } + } else { + node_new = node_old; + node_old = node_old->next; + } + } +} + +/** Returns the id of a being in the list */ +unsigned int get_id(NODE *node) { + return node->id; +} + +/** Find a NPC id based on its coordinates */ +unsigned int find_npc(unsigned short x, unsigned short y) { + NODE *node = head; + while(node) { + if((node->job>=46)&&(node->job<=125)) { // Check if is a NPC (only low job ids) + if((get_x(node->coordinates)==x)&&(get_y(node->coordinates)==y)) + return node->id; + else node = node->next; + } else node = node->next; + } + return 0; +} + +/** Find a MONSTER id based on its coordinates */ +unsigned int find_monster(unsigned short x, unsigned short y) { + NODE *node = head; + while(node) { + //if((node->job>=46)&&(node->job<=125)) { // Check if is a NPC (only low job ids) + if((get_x(node->coordinates)==x)&&(get_y(node->coordinates)==y)) + return node->id; + else node = node->next; + //} else node = node->next; + } + return 0; +} + +/** Return a specific id node */ +NODE *find_node(unsigned int id) { + NODE *node = head; + while(node) + if(node->id==id) + return node; + else node = node->next; + return NULL; +} + +/** Sort beings in vertical order using bubble sort */ +void sort() { + NODE *p, *q, *r, *s, *temp; + s = NULL; + + while(s!=head->next) { + r = p = head; + q = p->next; + + while(p!=s) { + if(get_y(p->coordinates)>get_y(q->coordinates)) { + if(p==head) { + temp = q->next; + q->next = p; + p->next = temp; + head = q; + r = q; + } else { + temp = q->next; + q->next = p; + p->next = temp; + r->next = q; + r = q; + } + } else { + r = p; + p = p->next; + } + q = p->next; + if(q==s)s = p; + } + } +} diff --git a/src/being.h b/src/being.h new file mode 100644 index 00000000..d1dcdb31 --- /dev/null +++ b/src/being.h @@ -0,0 +1,80 @@ +/** + + 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 + +*/ + +#ifndef _BEING_H +#define _BEING_H + +#include "./Net/protocol.h" + +#define ACTION_NODE 0 +#define PLAYER_NODE 1 +#define NPC_NODE 2 +#define MONSTER_NODE 3 + +struct PATH_NODE { + unsigned short x, y; + PATH_NODE *next; +}; + +struct NODE { + unsigned int id; + short job; + char coordinates[3]; + NODE *next; + unsigned char type; + unsigned char action; + unsigned char frame; + PATH_NODE *path; + char *speech; + unsigned char speech_time; + int speech_color; + short tick_time; + short speed; + unsigned char emotion; + unsigned char emotion_time; + int text_x, text_y; // temp solution to fix speech position +}; + +void empty(); +NODE *get_head(); +NODE *create_node(); +PATH_NODE *create_path_node(unsigned short x, unsigned short y); +void add_node(NODE *node); +NODE *find_node(unsigned int id); +void remove_node(unsigned int id); +unsigned int get_count(); +bool remove_being(char *data); +void remove_being(unsigned int id); +int get_beings_size(); +bool is_being(int id); +void popup_being(unsigned char type, char *data); +void move_being(char *data); +PATH_NODE *calculate_path(unsigned short src_x, unsigned short src_y, unsigned short dest_x, unsigned short dest_y); +unsigned int get_id(NODE *node); +unsigned int find_npc(unsigned short x, unsigned short y); +unsigned int find_monster(unsigned short x, unsigned short y); +void sort(); + +extern NODE *player_node; + +#endif 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); + } + } +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 00000000..8a48d046 --- /dev/null +++ b/src/game.h @@ -0,0 +1,92 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _GAME_H +#define _GAME_H + +#include <allegro.h> +#include <stdio.h> + +#include "main.h" +#include "./Gui/gui.h" +#include "./Gui/skill.h" +#include "./Graphic/SuperEagle.h" + +#define SPEECH_TIME 40 +#define EMOTION_TIME 40 +#define SPEECH_MAX_TIME 100 + +#define MAKECOL_WHITE makecol(255,255,255) +#define MAKECOL_BLACK makecol(0,0,0) + +#define GET_WALKING_SPEECH_XCOORDS ((get_x(node->coordinates)-map_x+13)*16-25-(alfont_text_length(gui_font, node->speech)/2)-player_x-coeff_x*(16-4*node->frame)) +#define GET_WALKING_SPEECH_YCOORDS ((get_y(node->coordinates)-map_y+6)*16-55-player_y-coeff_y*(16-4*node->frame)) + +#define GET_STANDING_SPEECH_XCOODRS ((get_x(node->coordinates)-map_x+13)*16-25-(alfont_text_length(gui_font, node->speech)/2)-player_x) +#define GET_STANDING_SPEECH_YCOORDS ((get_y(node->coordinates)-map_y+6)*16-55-player_y) + +#define PLAYERSETS 4 +#define SIT 17 +#define STAND 0 +#define WALK 1 +#define MONSTER_ATTACK 5 +#define MONSTER_DEAD 9 +#define ATTACK 7 +#define LOCK 254 +#define IDLE 255 + +#define SOUTH 0 +#define SW 1 +#define WEST 2 +#define NW 3 +#define NORTH 4 +#define NE 5 +#define EAST 6 +#define SE 7 + +extern char map_path[480]; +extern DIALOG skill_dialog[]; +extern int fps, frame; +extern char walk_status; +extern unsigned short src_x, src_y, x, y; +extern volatile int tick_time; + +void game(); +void do_init(); +void do_input(); +void do_parse(); +void do_graphic(); +void do_logic(); +void do_exit(); +int get_packet_length(short); + +char get_x_offset(char, char); +char get_y_offset(char, char); + +short get_elapsed_time(short start_time); + +#endif diff --git a/src/graphic/graphic.cpp b/src/graphic/graphic.cpp new file mode 100644 index 00000000..3a6e71a3 --- /dev/null +++ b/src/graphic/graphic.cpp @@ -0,0 +1,256 @@ +/** + + 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 + + By ElvenProgrammer aka Eugenio Favalli (umperio@users.sourceforge.net) + +*/ + +#include "graphic.h" + +#define TILESET_W 480 +#define TILESET_H 320 + +#ifdef WIN32 +#pragma warning (disable:4312) +#endif + +#include <allegro.h> +#include "../game.h" +#include "../map.h" +#include "../being.h" +#include "../Gui/chat.h" +#include "../Gui/inventory.h" +#include "../data/graphic/gfx_data.h" + +BITMAP *buffer, *double_buffer, *chat_background; +DATAFILE *tileset; +char page_num; +int map_x, map_y; +DIALOG_PLAYER *chat_player, *npc_player, *skill_player; +char speech[255] = ""; +char npc_text[1000] = ""; +TmwInventory inventory; +Chat chatlog("chatlog.txt", 20); +int show_npc_dialog = 0; +bool show_skill_dialog = false; + +DIALOG npc_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 300, 200, 260, 150, 0, 0, 0, 0, 0, 0, (char *)"NPC", NULL, NULL }, + { tmw_button_proc, 508, 326, 50, 20, 255, 0, 'c', D_EXIT, 0, 0, (char *)"&Close", NULL, NULL }, + { tmw_textbox_proc, 304, 224, 252, 100, 0, 0, 0, 0, 0, 0, npc_text, NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } +}; + +DIALOG chat_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_edit_proc, 0, 574, 592, 25, 0, 0, 'c', 0, 90, 0, speech, NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } +}; + +int get_x_offset(NODE *node) { + int offset = 0; + char direction = get_direction(node->coordinates); + if(node->action==WALK) { + if(direction==WEST || direction==EAST) { + offset = node->frame + 1; + if(offset==5)offset = 0; + offset *= 4; + if(direction==WEST) { + offset = -offset; + offset += 16; + } else offset -= 16; + } + } + return offset; +} + +int get_y_offset(NODE *node) { + int offset = 0; + char direction = get_direction(node->coordinates); + if(node->action==WALK) { + if(direction==SOUTH || direction==NORTH) { + offset = node->frame + 1; + if(offset==5)offset = 0; + offset *= 4; + if(direction==NORTH) { + offset = -offset; + offset += 16; + } else offset -= 16; + } + } + return offset; +} + +void init_graphic() { + tileset = load_datafile("./data/graphic/village.dat"); + //if(!(gfx_capabilities & GFX_HW_VRAM_BLIT))allegro_message("Not supporting HW accelerated blit"); + buffer = create_bitmap(SCREEN_W/2, SCREEN_H/2); + double_buffer = create_bitmap(SCREEN_W, SCREEN_H); + + alfont_set_font_size(gui_font, 16); + clear_bitmap(screen); + chat_background = create_bitmap(592, 100); + clear_to_color(chat_background, makecol(0,0,0)); + + // Initialize gui + chat_player = init_dialog(chat_dialog, -1); + npc_player = init_dialog(npc_dialog, -1); + position_dialog(npc_dialog, 300, 200); + skill_player = init_dialog(skill_dialog, -1); + gui_bitmap = double_buffer; + alfont_text_mode(-1); + inventory.create(0, 0); +} + +void do_graphic(void) { + + map_x = (get_x(player_node->coordinates)-13)*16+get_x_offset(player_node); + map_y = (get_y(player_node->coordinates)-9)*16+get_y_offset(player_node); + + int camera_x = map_x >> 4; + int camera_y = map_y >> 4; + + int offset_x = map_x & 15; + int offset_y = map_y & 15; + + sort(); + + for(int j=0;j<20;j++) + for(int i=0;i<26;i++) { + draw_rle_sprite(buffer, (RLE_SPRITE *)tileset[get_tile(i+camera_x, j+camera_y, 0)].dat, i*16-offset_x, j*16-offset_y); + if(get_tile(i+camera_x, j+camera_y, 1)!=0)draw_rle_sprite(buffer, (RLE_SPRITE *)tileset[get_tile(i+camera_x, j+camera_y, 1)].dat, i*16-offset_x, j*16-offset_y); + } + + NODE *node = get_head(); + NODE *old_node = NULL; + while(node) { + if((node->job>=100)&&(node->job<=110)) { // Draw a NPC + masked_blit((BITMAP *)graphic[NPCSET_BMP].dat, buffer, (get_direction(node->coordinates)/2+4*(node->job-100))*25, 0, (get_x(node->coordinates)-camera_x)*16-4-offset_x, (get_y(node->coordinates)-camera_y)*16-24-offset_y, 25, 40); + } else if(node->job<10) { // Draw a player + + node->text_x = (get_x(node->coordinates)-camera_x)*16-34+get_x_offset(node)-offset_x; + node->text_y = (get_y(node->coordinates)-camera_y)*16-36+get_y_offset(node)-offset_y; + masked_blit((BITMAP *)graphic[PLAYERSET_BMP].dat, buffer, 80*(get_direction(node->coordinates)/2), 60*(node->frame+node->action), node->text_x, node->text_y, 80, 60); + + if(node->emotion!=0) { + draw_sprite(buffer, (BITMAP *)emotions[node->emotion-1].dat, (get_x(node->coordinates)-camera_x)*16-5+get_x_offset(node)-offset_x, (get_y(node->coordinates)-camera_y)*16-45+get_y_offset(node)-offset_y); + node->emotion_time--; + if(node->emotion_time==0) + node->emotion = 0; + } + + if(node->action!=STAND && node->action!=SIT) { + node->frame = (get_elapsed_time(node->tick_time)*4)/(node->speed); + if(node->frame>=4) { + node->frame = 0; + node->action = STAND; + node->tick_time; + if(node->id==player_node->id) + walk_status = 0; + } + } + + } else if(node->job==45) { // Draw a warp + //rectfill(buffer, (get_x(node->coordinates)-map_x)*16-player_x-get_x_offset(node->frame, get_direction(node->coordinates)), (get_y(node->coordinates)-map_y)*16-player_y-get_y_offset(node->frame, get_direction(node->coordinates)), (get_x(node->coordinates)-map_x)*16-player_x-get_x_offset(node->frame, get_direction(node->coordinates))+16, (get_y(node->coordinates)-map_y)*16-player_y-get_y_offset(node->frame, get_direction(node->coordinates))+16, makecol(0,0,255)); + } else { // Draw a monster + + node->text_x = (get_x(node->coordinates)-camera_x)*16-20+get_x_offset(node)-offset_x; + node->text_y = (get_y(node->coordinates)-camera_y)*16-25+get_y_offset(node)-offset_y; + + if(node->action==MONSTER_DEAD)node->frame = 0; + masked_blit((BITMAP *)graphic[MOBSET_BMP].dat, buffer, (get_direction(node->coordinates)/2)*60, 60*(node->frame+node->action), node->text_x, node->text_y, 60, 60); + if(node->action!=STAND) { + node->frame = (get_elapsed_time(node->tick_time)*4)/(node->speed); + if(node->frame>=4) { + if(node->action!=MONSTER_DEAD) { + if(node->path && node->action!=MONSTER_DEAD) { + PATH_NODE *old = node->path; + set_coordinates(node->coordinates, node->path->x, node->path->y, 0); + node->path = node->path->next; + if(old!=NULL) + free(old); + } else node->action = STAND; + if(node->action!=MONSTER_DEAD)node->frame = 0; + node->tick_time = tick_time; + } + } + } + } + old_node = node; + node = node->next; + if(old_node->action==MONSTER_DEAD && old_node->frame>=4) + remove_node(old_node->id); + } + + for(int j=0;j<20;j++) + for(int i=0;i<26;i++) { + if(get_tile(i+camera_x, j+camera_y, 2)!=0)draw_rle_sprite(buffer, (RLE_SPRITE *)tileset[get_tile(i+camera_x, j+camera_y, 2)].dat, i*16-offset_x, j*16-offset_y); + } + + stretch_blit(buffer, double_buffer, 0, 0, 400, 300, 0, 0, 800, 600); + + // Draw player speech + node = get_head(); + while(node) { + if(node->speech!=NULL) { + alfont_textprintf_aa(double_buffer, gui_font, node->text_x+260-alfont_text_length(gui_font, node->speech)/2, node->text_y+100, node->speech_color, "%s", node->speech); + + node->speech_time--; + if(node->speech_time==0) { + free(node->speech); + node->speech = NULL; + } + } + node = node->next; + } + + inventory.draw(double_buffer); + + set_trans_blender(0, 0, 0, 110); + draw_trans_sprite(double_buffer, chat_background, 0, SCREEN_H-125); + + chatlog.chat_draw(double_buffer, 8, gui_font); + gui_update(chat_player); + + if(show_npc_dialog) { + dialog_message(npc_dialog,MSG_DRAW,0,0); + if(!(show_npc_dialog = gui_update(npc_player)))strcpy(npc_text, ""); + } + + if(show_skill_dialog) { + update_skill_dialog(); + if(gui_update(skill_player)==0)show_skill_dialog = false; + } + + + alfont_textprintf_aa(double_buffer, gui_font, 0, 0, MAKECOL_WHITE, "FPS:%i", fps); + + blit(double_buffer, screen, 0, 0, 0, 0, 800, 600); + + frame++; +} + +void exit_graphic() { + shutdown_dialog(npc_player); + shutdown_dialog(chat_player); + shutdown_dialog(skill_player); +} diff --git a/src/graphic/graphic.h b/src/graphic/graphic.h new file mode 100644 index 00000000..c50def22 --- /dev/null +++ b/src/graphic/graphic.h @@ -0,0 +1,50 @@ +/** + + 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 + + By ElvenProgrammer aka Eugenio Favalli (umperio@users.sourceforge.net) + +*/ + +#ifndef _GRAPHIC_H +#define _GRAPHIC_H + +#ifdef WIN32 +#pragma warning (disable:4312) +#endif + +#include <allegro.h> +#include "../Gui/chat.h" +#include "../Gui/inventory.h" + +extern BITMAP *buffer, *double_buffer; +extern char speech[255]; +extern char npc_text[1000]; +extern Chat chatlog; +extern bool show_skill_dialog; +extern int show_npc_dialog; +extern TmwInventory inventory; +extern int map_x, map_y; + +void do_graphic(void); +void init_graphic(void); +void exit_graphic(void); + +#endif diff --git a/src/graphic/super_eagle.cpp b/src/graphic/super_eagle.cpp new file mode 100644 index 00000000..78bb6fea --- /dev/null +++ b/src/graphic/super_eagle.cpp @@ -0,0 +1,450 @@ +/** + + 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 "SuperEagle.h" + +static uint32 colorMask = 0xF7DEF7DE; +static uint32 lowPixelMask = 0x08210821; +static uint32 qcolorMask = 0xE79CE79C; +static uint32 qlowpixelMask = 0x18631863; +static uint32 redblueMask = 0xF81F; +static uint32 greenMask = 0x7E0; +static int PixelsPerMask = 2; +static int xsai_depth = 0; + +int Init_SuperEagle(int d) { + int minr = 0, ming = 0, minb = 0; + int i; + + if (d != 15 && d != 16 && d != 24 && d != 32) + return -1; + + /* Get lowest color bit */ + for (i = 0; i < 255; i++) { + if (!minr) + minr = makecol(i, 0, 0); + if (!ming) + ming = makecol(0, i, 0); + if (!minb) + minb = makecol(0, 0, i); + } + + colorMask = (makecol_depth(d, 255, 0, 0) - minr) | (makecol_depth(d, 0, 255, 0) - ming) | (makecol_depth(d, 0, 0, 255) - minb); + lowPixelMask = minr | ming | minb; + qcolorMask = (makecol_depth(d, 255, 0, 0) - 3 * minr) | (makecol_depth(d, 0, 255, 0) - 3 * ming) | (makecol_depth(d, 0, 0, 255) - 3 * minb); + qlowpixelMask = (minr * 3) | (ming * 3) | (minb * 3); + redblueMask = makecol_depth(d, 255, 0, 255); + greenMask = makecol_depth(d, 0, 255, 0); + + PixelsPerMask = (d <= 16) ? 2 : 1; + + if (PixelsPerMask == 2) { + colorMask |= (colorMask << 16); + qcolorMask |= (qcolorMask << 16); + lowPixelMask |= (lowPixelMask << 16); + qlowpixelMask |= (qlowpixelMask << 16); + } + + TRACE("Color Mask: 0x%lX\n", colorMask); + TRACE("Low Pixel Mask: 0x%lX\n", lowPixelMask); + TRACE("QColor Mask: 0x%lX\n", qcolorMask); + TRACE("QLow Pixel Mask: 0x%lX\n", qlowpixelMask); + + xsai_depth = d; + + return 0; +} + +/** unused /- kth5 +static int GetResult1(uint32 A, uint32 B, uint32 C, uint32 D) { + int x = 0; + int y = 0; + int r = 0; + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r += 1; + if (y <= 1) + r -= 1; + return r; +} + +static int GetResult2(uint32 A, uint32 B, uint32 C, uint32 D, uint32 E) { + int x = 0; + int y = 0; + int r = 0; + if (A == C) + x += 1; + else if (B == C) + y += 1; + if (A == D) + x += 1; + else if (B == D) + y += 1; + if (x <= 1) + r -= 1; + if (y <= 1) + r += 1; + return r; +}*/ + + +#define GET_RESULT(A, B, C, D) ((A != C || A != D) - (B != C || B != D)) + +#define INTERPOLATE(A, B) (((A & colorMask) >> 1) + ((B & colorMask) >> 1) + (A & B & lowPixelMask)) + +#define Q_INTERPOLATE(A, B, C, D) ((A & qcolorMask) >> 2) + ((B & qcolorMask) >> 2) + ((C & qcolorMask) >> 2) + ((D & qcolorMask) >> 2) \ + + ((((A & qlowpixelMask) + (B & qlowpixelMask) + (C & qlowpixelMask) + (D & qlowpixelMask)) >> 2) & qlowpixelMask) + + +/* Clipping Macro, stolen from Allegro, modified to work with 2xSaI */ +#define BLIT_CLIP2(src, dest, s_x, s_y, d_x, d_y, w, h, xscale, yscale) \ + /* check for ridiculous cases */ \ + if ((s_x >= src->cr) || (s_y >= src->cb) || \ + (d_x >= dest->cr) || (d_y >= dest->cb)) \ + return; \ + \ + if ((s_x + w < src->cl) || (s_y + h < src->ct) || \ + (d_x + w * xscale < dest->cl) || (d_y + h * yscale < dest->ct)) \ + return; \ + \ + if (xscale < 1 || yscale < 1) \ + return; \ + \ + /* clip src left */ \ + if (s_x < src->cl) { \ + w += s_x; \ + d_x -= s_x * xscale; \ + s_x = src->cl; \ + } \ + \ + /* clip src top */ \ + if (s_y < src->ct) { \ + h += s_y; \ + d_y -= s_y * yscale; \ + s_y = src->ct; \ + } \ + \ + /* clip src right */ \ + if (s_x + w > src->cr) \ + w = src->cr - s_x; \ + \ + /* clip src bottom */ \ + if (s_y + h > src->cb) \ + h = src->cb - s_y; \ + \ + /* clip dest left */ \ + if (d_x < dest->cl) { \ + d_x -= dest->cl; \ + w += d_x / xscale; \ + s_x -= d_x / xscale; \ + d_x = dest->cl; \ + } \ + \ + /* clip dest top */ \ + if (d_y < dest->ct) { \ + d_y -= dest->ct; \ + h += d_y / yscale; \ + s_y -= d_y / yscale; \ + d_y = dest->ct; \ + } \ + \ + /* clip dest right */ \ + if (d_x + w * xscale > dest->cr) \ + w = (dest->cr - d_x) / xscale; \ + \ + /* clip dest bottom */ \ + if (d_y + h * yscale > dest->cb) \ + h = (dest->cb - d_y) / yscale; \ + \ + /* bottle out if zero size */ \ + if ((w <= 0) || (h <= 0)) \ + return; + + +static unsigned char *src_line[4]; +static unsigned char *dst_line[2]; + + + +void SuperEagle(BITMAP * src, BITMAP * dest, int s_x, int s_y, int d_x, int d_y, int w, int h) { + int sbpp, dbpp; + + BITMAP *dst2 = NULL; + + if (!src || !dest) + return; + + sbpp = bitmap_color_depth(src); + dbpp = bitmap_color_depth(dest); + + if ((sbpp != xsai_depth) || (sbpp != dbpp)) /* Must be same color depth */ + return; + + BLIT_CLIP2(src, dest, s_x, s_y, d_x, d_y, w, h, 2, 2); + + if (w < 4 || h < 4) { /* Image is too small to be 2xSaI'ed. */ + stretch_blit(src, dest, s_x, s_y, w, h, d_x, d_y, w * 2, h * 2); + return; + } + + sbpp = BYTES_PER_PIXEL(sbpp); + if (d_x || d_y) + dst2 = create_sub_bitmap(dest, d_x, d_y, w * 2, h * 2); + + SuperEagle_ex(src->line[s_y] + s_x * sbpp, (unsigned int)(src->line[1] - src->line[0]), NULL, dst2 ? dst2 : dest, w, h); + + if (dst2) + destroy_bitmap(dst2); + + return; +} + +void SuperEagle_ex(uint8 *src, uint32 src_pitch, uint8 *unused, BITMAP *dest, uint32 width, uint32 height) { + + int j, v; + unsigned int x, y; + int sbpp = BYTES_PER_PIXEL(bitmap_color_depth(dest)); + unsigned long color[12]; + + /* Point to the first 3 lines. */ + src_line[0] = src; + src_line[1] = src; + src_line[2] = src + src_pitch; + src_line[3] = src + src_pitch * 2; + + /* Can we write the results directly? */ + if (is_video_bitmap(dest) || is_planar_bitmap(dest)) { + dst_line[0] = (unsigned char *)malloc(sizeof(char) * sbpp * width); + dst_line[1] = (unsigned char *)malloc(sizeof(char) * sbpp * width); + v = 1; + } + else { + dst_line[0] = dest->line[0]; + dst_line[1] = dest->line[1]; + v = 0; + } + + /* Set destination */ + bmp_select(dest); + + x = 0, y = 0; + + if (PixelsPerMask == 2) { + unsigned short *sbp; + sbp = (unsigned short*)src_line[0]; + color[0] = *sbp; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = *(sbp + 1); color[5] = *(sbp + 2); + sbp = (unsigned short*)src_line[2]; + color[6] = *sbp; color[7] = color[6]; color[8] = *(sbp + 1); color[9] = *(sbp + 2); + sbp = (unsigned short*)src_line[3]; + color[10] = *sbp; color[11] = *(sbp + 1); + } + else { + unsigned long *lbp; + lbp = (unsigned long*)src_line[0]; + color[0] = *lbp; color[1] = color[0]; color[2] = color[0]; color[3] = color[0]; + color[4] = *(lbp + 1); color[5] = *(lbp + 2); + lbp = (unsigned long*)src_line[2]; + color[6] = *lbp; color[7] = color[6]; color[8] = *(lbp + 1); color[9] = *(lbp + 2); + lbp = (unsigned long*)src_line[3]; + color[10] = *lbp; color[11] = *(lbp + 1); + } + + for (y = 0; y < height; y++) { + + /* Todo: x = width - 2, x = width - 1 */ + + for (x = 0; x < width; x++) { + unsigned long product1a, product1b, product2a, product2b; + +//--------------------------------------- B1 B2 0 1 +// 4 5 6 S2 -> 2 3 4 5 +// 1 2 3 S1 6 7 8 9 +// A1 A2 10 11 + + if (color[7] == color[4] && color[3] != color[8]) { + product1b = product2a = color[7]; + + if ((color[6] == color[7]) || (color[4] == color[1])) + product1a = INTERPOLATE(color[7], INTERPOLATE(color[7], color[3])); + else + product1a = INTERPOLATE(color[3], color[4]); + + if ((color[4] == color[5]) || (color[7] == color[10])) + product2b = INTERPOLATE(color[7], INTERPOLATE(color[7], color[8])); + else + product2b = INTERPOLATE(color[7], color[8]); + } + else if (color[3] == color[8] && color[7] != color[4]) { + product2b = product1a = color[3]; + + if ((color[0] == color[3]) || (color[5] == color[9])) + product1b = INTERPOLATE(color[3], INTERPOLATE(color[3], color[4])); + else + product1b = INTERPOLATE(color[3], color[1]); + + if ((color[8] == color[11]) || (color[2] == color[3])) + product2a = INTERPOLATE(color[3], INTERPOLATE(color[3], color[2])); + else + product2a = INTERPOLATE(color[7], color[8]); + + } + else if (color[3] == color[8] && color[7] == color[4]) { + register int r = 0; + + r += GET_RESULT(color[4], color[3], color[6], color[10]); + r += GET_RESULT(color[4], color[3], color[2], color[0]); + r += GET_RESULT(color[4], color[3], color[11], color[9]); + r += GET_RESULT(color[4], color[3], color[1], color[5]); + + if (r > 0) { + product1b = product2a = color[7]; + product1a = product2b = INTERPOLATE(color[3], color[4]); + } + else if (r < 0) { + product2b = product1a = color[3]; + product1b = product2a = INTERPOLATE(color[3], color[4]); + } + else { + product2b = product1a = color[3]; + product1b = product2a = color[7]; + } + } + else { + product2b = product1a = INTERPOLATE(color[7], color[4]); + product2b = Q_INTERPOLATE(color[8], color[8], color[8], product2b); + product1a = Q_INTERPOLATE(color[3], color[3], color[3], product1a); + + product2a = product1b = INTERPOLATE(color[3], color[8]); + product2a = Q_INTERPOLATE(color[7], color[7], color[7], product2a); + product1b = Q_INTERPOLATE(color[4], color[4], color[4], product1b); + } + + if (PixelsPerMask == 2) { + *((unsigned long *) (&dst_line[0][x * 4])) = product1a | (product1b << 16); + *((unsigned long *) (&dst_line[1][x * 4])) = product2a | (product2b << 16); + } + else { + *((unsigned long *) (&dst_line[0][x * 8])) = product1a; + *((unsigned long *) (&dst_line[0][x * 8 + 4])) = product1b; + *((unsigned long *) (&dst_line[1][x * 8])) = product2a; + *((unsigned long *) (&dst_line[1][x * 8 + 4])) = product2b; + } + + /* Move color matrix forward */ + color[0] = color[1]; + color[2] = color[3]; color[3] = color[4]; color[4] = color[5]; + color[6] = color[7]; color[7] = color[8]; color[8] = color[9]; + color[10] = color[11]; + + if (x < width - 2) { + x += 2; + if (PixelsPerMask == 2) { + color[1] = *(((unsigned short*)src_line[0]) + x); + if (x < width) { + color[5] = *(((unsigned short*)src_line[1]) + x + 1); + color[9] = *(((unsigned short*)src_line[2]) + x + 1); + } + color[11] = *(((unsigned short*)src_line[3]) + x); + } + else { + color[1] = *(((unsigned long*)src_line[0]) + x); + if (x < width) { + color[5] = *(((unsigned long*)src_line[1]) + x + 1); + color[9] = *(((unsigned long*)src_line[2]) + x + 1); + } + color[11] = *(((unsigned long*)src_line[3]) + x); + } + x -= 2; + } + } + + /* We're done with one line, so we shift the source lines up */ + src_line[0] = src_line[1]; + src_line[1] = src_line[2]; + src_line[2] = src_line[3]; + + /* Read next line */ + if (y + 3 >= height) + src_line[3] = src_line[2]; + else + src_line[3] = src_line[2] + src_pitch; + + /* Then shift the color matrix up */ + if (PixelsPerMask == 2) { + unsigned short *sbp; + sbp = (unsigned short*)src_line[0]; + color[0] = *sbp; color[1] = *(sbp + 1); + sbp = (unsigned short*)src_line[1]; + color[2] = *sbp; color[3] = color[2]; color[4] = *(sbp + 1); color[5] = *(sbp + 2); + sbp = (unsigned short*)src_line[2]; + color[6] = *sbp; color[7] = color[6]; color[8] = *(sbp + 1); color[9] = *(sbp + 2); + sbp = (unsigned short*)src_line[3]; + color[10] = *sbp; color[11] = *(sbp + 1); + } + else { + unsigned long *lbp; + lbp = (unsigned long*)src_line[0]; + color[0] = *lbp; color[1] = *(lbp + 1); + lbp = (unsigned long*)src_line[1]; + color[2] = *lbp; color[3] = color[2]; color[4] = *(lbp + 1); color[5] = *(lbp + 2); + lbp = (unsigned long*)src_line[2]; + color[6] = *lbp; color[7] = color[6]; color[8] = *(lbp + 1); color[9] = *(lbp + 2); + lbp = (unsigned long*)src_line[3]; + color[10] = *lbp; color[11] = *(lbp + 1); + } + + + /* Write the 2 lines, if not already done so */ + if (v) { + unsigned long dst_addr; + + dst_addr = bmp_write_line(dest, y * 2); + for (j = 0; j < dest->w * sbpp; j += sizeof(long)) + bmp_write32(dst_addr + j, *((unsigned long *) (dst_line[0] + j))); + + dst_addr = bmp_write_line(dest, y * 2 + 1); + for (j = 0; j < dest->w * sbpp; j += sizeof(long)) + bmp_write32(dst_addr + j, *((unsigned long *) (dst_line[1] + j))); + } + else { + if (y < height - 1) { + dst_line[0] = dest->line[y * 2 + 2]; + dst_line[1] = dest->line[y * 2 + 3]; + } + } + } + bmp_unwrite_line(dest); + + if (v) { + free(dst_line[0]); + free(dst_line[1]); + } +} diff --git a/src/graphic/super_eagle.h b/src/graphic/super_eagle.h new file mode 100644 index 00000000..64c763db --- /dev/null +++ b/src/graphic/super_eagle.h @@ -0,0 +1,44 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _SUPER_EAGLE_H +#define _SUPER_EAGLE_H + +#include <string.h> +#include <allegro.h> +#include <allegro/internal/aintern.h> + +#define uint32 unsigned long +#define uint16 unsigned short +#define uint8 unsigned char + +void SuperEagle(BITMAP * src, BITMAP * dest, int s_x, int s_y, int d_x, int d_y, int w, int h); +void SuperEagle_ex(uint8 *src, uint32 src_pitch, uint8 *unused, BITMAP *dest, uint32 width, uint32 height); + +int Init_SuperEagle(int d); + +#endif diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp new file mode 100644 index 00000000..e58a8d73 --- /dev/null +++ b/src/gui/char_select.cpp @@ -0,0 +1,221 @@ +/** + + 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 "char_select.h" +#include "../Graphic/graphic.h" + +char button_state[3]; +char address[41]; +char name[25]; + +DIALOG char_select_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 300, 240, 200, 208, 0, -1, 0, 0, 0, 0, (char*)"Char select", NULL, NULL }, + { tmw_text_proc, 308, 268, 192, 10, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, + { tmw_bitmap_proc, 304, 282, 192, 70, 0, 0, 0, 0, 80, 60, playerset, NULL, NULL }, + { tmw_button_proc, 398, 426, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char*)"&Ok", NULL, NULL }, + { tmw_button_proc, 446, 426, 44, 18, 0, -1, 'c', D_EXIT, -1, 0, (char*)"&Cancel", NULL, NULL }, + { tmw_button_proc, 304, 426, 44, 18, 0, 0, 0, D_EXIT, 0, 0, button_state, NULL, NULL }, +/* { gui_button_proc, 304, 356, 20, 20, 0, 0, 0, 0, 0, 0, "<", NULL, NULL }, + { gui_button_proc, 328, 356, 20, 20, 0, 0, 0, 0, 0, 0, ">", NULL, NULL }, */ + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, +}; + +DIALOG char_create_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 276, 240, 248, 116, 0, -1, 0, 0, 0, 0, (char*)"Char create", NULL, NULL }, + { tmw_text_proc, 280, 268, 192, 20, 0, 0, 0, 0, 0, 0, (char *)"Name: ", NULL, NULL }, + { tmw_edit_proc, 336, 268, 162, 20, 0, 0, 0, 0, 24, 0, name, NULL, NULL }, + { tmw_button_proc, 398, 334, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char*)"&Ok", NULL, NULL }, + { tmw_button_proc, 446, 334, 44, 18, 0, -1, 'c', D_EXIT, -1, 0, (char*)"&Cancel", NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, +}; + +void char_select() { + state = LOGIN; + if(n_character>0) { + char_select_dialog[1].dp = char_info->name; + char_select_dialog[1].x = 400-alfont_text_length(gui_font, char_info->name)/2; + char_select_dialog[2].dp = playerset; + if(playerset==NULL)ok("Error", "Playerset not loaded"); + strcpy(button_state, "Del"); + char_select_dialog[3].flags &= D_CLOSE; + } else { + char *temp = (char *)malloc(3); + strcpy(temp, ""); + char_select_dialog[1].dp = temp; + char_select_dialog[3].flags |= D_DISABLED; + char_select_dialog[2].dp = NULL; + strcpy(button_state, "New"); + } + //centre_dialog(char_select_dialog); + DIALOG_PLAYER *player = init_dialog(char_select_dialog, -1); + int gui_exit = 1; + while ((!key[KEY_ESC])&&(gui_exit)&&(!key[KEY_ENTER])) { + clear_bitmap(buffer); + if(stretch_mode!=0)blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, 80, 60, 640, 480); + else blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, -120, -90, 640, 480); + gui_exit = gui_update(player); + blit(buffer, screen, 0, 0, 0, 0, 800, 600); + } + gui_exit = shutdown_dialog(player); + if((gui_exit==3)||(key[KEY_ENTER]))server_char_select(); + else if(gui_exit==4)close_session(); + else if(gui_exit==5)server_char_delete(); + if(state==LOGIN)close_session(); +} + +void server_char_select() { + // Request character selection + WFIFOW(0) = net_w_value(0x0066); + WFIFOB(2) = net_b_value(0); + WFIFOSET(3); + + while((in_size<3)||(out_size>0))flush(); + log_hex("Char_Select_Packet", "Packet_ID", RFIFOW(0)); + log_int("Char_Select_Packet", "Packet_length", get_length(RFIFOW(0))); + log_int("Char_Select_Packet", "Packet_in_size", RFIFOW(2)); + log_int("Char_Select_Packet", "In_size", in_size); + + if(RFIFOW(0)==0x0071) { + while(in_size<28)flush(); + char_ID = RFIFOL(2); + memset(map_path, '\0', 480); + append_filename(map_path, "./data/map/", RFIFOP(6), 480); + map_address = RFIFOL(22); + map_port = RFIFOW(26); + state = GAME; + + log("Player", "map", map_name); + log("Char_Select_packet", "server_address", iptostring(map_address)); + log_int("Char_Select_packet", "server_port", map_port); + RFIFOSKIP(28); + close_session(); + } else if(RFIFOW(0)==0x006c) { + switch(RFIFOB(2)) { + case 0: + ok("Error", "Access denied"); + break; + case 1: + ok("Error", "Cannot use this ID"); + break; + } + state = CHAR_SELECT; + RFIFOSKIP(3); + } + // Todo: add other packets +} + +void server_char_delete() { + state = CHAR_SELECT; + // Delete a character + if(!strcmp(button_state, "Del")) { + if(yes_no("Confirm", "Are you sure?")==0) { + // Request character deletion + WFIFOW(0) = net_w_value(0x0068); + WFIFOL(2) = net_l_value(char_info[0].id); + WFIFOSET(46); + + while((in_size<2)||(out_size>0))flush(); + if(RFIFOW(0)==0x006f) { + RFIFOSKIP(2); + ok("Info", "Player deleted"); + free(char_info); + n_character = 0; + } else if(RFIFOW(0)==0x006c) { + switch(RFIFOB(2)) { + case 0: + ok("Error", "Access denied"); + break; + case 1: + ok("Error", "Cannot use this ID"); + break; + } + RFIFOSKIP(3); + } else ok("Error", "Unknown error"); + } + // Create a new character + } else { + centre_dialog(char_create_dialog); + DIALOG_PLAYER *player = init_dialog(char_create_dialog, -1); + int gui_exit = 1; + while ((!key[KEY_ESC])&&(gui_exit)) { + clear_bitmap(buffer); + if(stretch_mode!=0)blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, 80, 60, 640, 480); + else blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, -120, -90, 640, 480); + gui_exit = gui_update(player); + blit(buffer, screen, 0, 0, 0, 0, 800, 600); + } + gui_exit = shutdown_dialog(player); + if(gui_exit==3) { + WFIFOW(0) = net_w_value(0x0067); + strcpy(WFIFOP(2), name); + WFIFOB(26) = net_b_value(5); + WFIFOB(27) = net_b_value(5); + WFIFOB(28) = net_b_value(5); + WFIFOB(29) = net_b_value(5); + WFIFOB(30) = net_b_value(5); + WFIFOB(31) = net_b_value(5); + WFIFOB(32) = net_b_value(0); + WFIFOW(33) = net_w_value(1); + WFIFOW(35) = net_w_value(1); + WFIFOSET(37); + + while((in_size<3)||(out_size>0))flush(); + if(RFIFOW(0)==0x006d) { + while(in_size<108)flush(); + char_info = (CHAR_SEL_INFO *)malloc(sizeof(CHAR_SEL_INFO)); + char_info->id = account_ID; + memset(char_info->name, '\0', 24); + strcpy(char_info[0].name, RFIFOP(2+74)); + char_info->hp = RFIFOW(2+42); + char_info->max_hp = RFIFOW(2+44); + char_info->sp = RFIFOW(2+46); + char_info->max_sp = RFIFOW(2+48); + char_info->job_lv = RFIFOL(2+16); + char_info->job_xp = RFIFOL(2+12); + char_info->lv = RFIFOW(2+58); + char_info->xp = RFIFOL(2+4); + char_info->zeny = RFIFOL(2+8); + char_info->STR = RFIFOB(2+98); + char_info->AGI = RFIFOB(2+99); + char_info->VIT = RFIFOB(2+100); + char_info->INT = RFIFOB(2+101); + char_info->DEX = RFIFOB(2+102); + char_info->LUK = RFIFOB(2+103); + RFIFOSKIP(108); + n_character++; + } else if(RFIFOW(0)==0x006c) { + switch(RFIFOB(2)) { + case 0: + ok("Error", "Access denied"); + break; + case 1: + ok("Error", "Cannot use this ID"); + break; + } + RFIFOSKIP(3); + } else ok("Error", "Unknown error"); + } + } +} diff --git a/src/gui/char_select.h b/src/gui/char_select.h new file mode 100644 index 00000000..94a8e820 --- /dev/null +++ b/src/gui/char_select.h @@ -0,0 +1,41 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _CHAR_SELECT_H +#define _CHAR_SELECT_H + +#include <allegro.h> + +#include "../main.h" +#include "../Net/network.h" +#include "gui.h" + +void char_select(); +void server_char_select(); +void server_char_delete(); + +#endif diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp new file mode 100644 index 00000000..36ef5d53 --- /dev/null +++ b/src/gui/char_server.cpp @@ -0,0 +1,141 @@ +/** + + 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 "char_server.h" +#include "../Graphic/graphic.h" + +char server[30]; + +DIALOG char_server_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 300, 240, 200, 104, 0, -1, 0, 0, 0, 0, (char*)"Server select", NULL, NULL }, + { tmw_list_proc, 304, 262, 192, 55, 0, 0, 0, 0, 0, 0, (char*)server_list, NULL, NULL }, + { tmw_button_proc, 398, 322, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char*)"&Ok", NULL, NULL }, + { tmw_button_proc, 446, 322, 44, 18, 0, -1, 'c', D_EXIT, -1, 0, (char*)"&Cancel", NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, +}; + +/** Helper function to create server list */ +char *server_list(int index, int *size) { + static char buffer[30]; + if (index < 0) { + *size = n_server; + return NULL; + } + sprintf(buffer, "%s (%i)", server_info[index].name, server_info[index].online_users); + return buffer; +} + +void char_server() { + state = LOGIN; + centre_dialog(char_server_dialog); + DIALOG_PLAYER *player = init_dialog(char_server_dialog, -1); + if(!player)ok("Error", "Unable to initialize login dialog"); + int gui_exit = 1; + if(n_server==0)char_server_dialog[2].flags |= D_DISABLED; + while ((!key[KEY_ESC])&&(gui_exit)&&(!key[KEY_ENTER])) { + clear_bitmap(buffer); + if(stretch_mode!=0)blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, 80, 60, 640, 480); + else blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, -120, -90, 640, 480); + gui_exit = gui_update(player); + blit(buffer, screen, 0, 0, 0, 0, 800, 600); + } + gui_exit = shutdown_dialog(player); + if((gui_exit==2)||(key[KEY_ENTER])) { + server_char_server(); + } +} + +void server_char_server() { + int ret; + state = LOGIN; + + // Connect to char server + ret = open_session(iptostring(server_info[char_server_dialog[1].d1].address), server_info[char_server_dialog[1].d1].port); + if(ret==SOCKET_ERROR) { + ok("Error", "Unable to connect to char server"); + return; + } + + // Send login infos + WFIFOW(0) = net_w_value(0x0065); + WFIFOL(2) = net_l_value(account_ID); + WFIFOL(6) = net_l_value(session_ID1); + WFIFOL(10) = net_l_value(session_ID2); + WFIFOW(14) = 0; + WFIFOB(16) = net_b_value(sex); + WFIFOSET(17); + + while((in_size<4)||(out_size>0))flush(); + RFIFOSKIP(4); + + while(in_size<3)flush(); + + if(RFIFOW(0)==0x006b) { + while(in_size<RFIFOW(2))flush(); + n_character = (RFIFOW(2)-24)/106; + char_info = (CHAR_SEL_INFO *)malloc(sizeof(CHAR_SEL_INFO)*n_character); + for(int i=0;i<n_character;i++) { + char_info[i].id = RFIFOL(24+106*i); + strcpy(char_info[i].name, RFIFOP(24+106*i+74)); + char_info[i].hp = RFIFOW(24+106*i+42); + char_info[i].max_hp = RFIFOW(24+106*i+44); + char_info[i].xp = RFIFOL(24+106*i+4); + char_info[i].zeny = RFIFOL(24+106*i+8); + char_info[i].job_xp = RFIFOL(24+106*i+16); + char_info[i].job_lv = RFIFOL(24+106*i+24); + char_info[i].sp = RFIFOW(24+106*i+46); + char_info[i].max_sp = RFIFOW(24+106*i+48); + char_info[i].lv = RFIFOW(24+106*i+58); + char_info[i].STR = RFIFOB(24+106*i+98); + char_info[i].AGI = RFIFOB(24+106*i+99); + char_info[i].VIT = RFIFOB(24+106*i+100); + char_info[i].INT = RFIFOB(24+106*i+101); + char_info[i].DEX = RFIFOB(24+106*i+102); + char_info[i].LUK = RFIFOB(24+106*i+103); + } + state = CHAR_SELECT; + + log("Player", "name", char_info->name); + log_hex("Char_Server_Packet", "Packet_ID", RFIFOW(0)); + log_int("Char_Server_Packet", "Packet_length", RFIFOW(2)); + + + RFIFOSKIP(RFIFOW(2)); + } else if(RFIFOW(0)==0x006c) { + switch(RFIFOB(2)) { + case 0: + ok("Error", "Access denied"); + break; + case 1: + ok("Error", "Cannot use this ID"); + break; + default: + ok("Error", "Rejected from server"); + break; + } + RFIFOSKIP(3); + close_session(); + } else ok("Error", "Unknown error"); + // Todo: add other packets +} diff --git a/src/gui/char_server.h b/src/gui/char_server.h new file mode 100644 index 00000000..866f5070 --- /dev/null +++ b/src/gui/char_server.h @@ -0,0 +1,41 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _CHAR_SEL_SERVER_H +#define _CHAR_SEL_SERVER_H + +#include <allegro.h> + +#include "../main.h" +#include "../Net/network.h" +#include "gui.h" + +void char_server(); +void server_char_server(); +char *server_list(int index, int *size); + +#endif diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp new file mode 100644 index 00000000..2f0385c9 --- /dev/null +++ b/src/gui/chat.cpp @@ -0,0 +1,303 @@ +/** + + 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 + +*/ + +/** + Simple ChatLog Object v0.5 (i'd say...) + + Bestviewd w/ Bitstream Vera Sans Mono @ 9pt and a tab-width of 2 spaces + + Author: kth5 aka Alexander Baldeck + pipe your questions, suggestions and flames to: kth5@gawab.com +*/ + +#include "../main.h" +#include "chat.h" +#include <list> +#include <string> +#include <fstream> + +using namespace std; + + +Chat::Chat(const char * logfile, int item_num) { + chatlog_file.open(logfile, ios::out | ios::app); + items = 0; + items_keep = item_num; +} + +Chat::~Chat() { + chatlog_file.flush(); + chatlog_file.close(); +} + +void Chat::chat_dlgrsize(int) { +} + +/** adds a line of text to our message list + + string line -> message text + int own -> type of message (usually the owner-type) + ALFONT_FONT * font -> font that'll be used to draw the text later + + NOTE: + to all of you who wonder why the font needs to be passed, simple. + i already store the width in pixel in the list rather than + calculating it again and again on every draw event. ;-) +*/ +void Chat::chat_log(string line, int own, ALFONT_FONT * font) { + int pos; + CHATLOG tmp; + + if(items<=items_keep) + items++; // delete overhead from the end of the list + else + chatlog.pop_back(); + + pos = 0; + pos = (int)line.find(" : ", 0); + if(pos > 0) { + tmp.nick = line.substr(0,pos); + switch(own) { + case ACT_IS : + tmp.nick += CAT_IS; + break; + case ACT_WHISPER : + tmp.nick += CAT_WHISPER; + break; + default : + tmp.nick += CAT_NORMAL; + } + tmp.width = TEXT_GETWIDTH(tmp.nick.c_str())+2; + line.erase(0,pos+3); + }else { + tmp.nick = ""; + tmp.width = 1; + } + tmp.own = own; + tmp.text = line; + + chatlog_file << tmp.nick << tmp.text << "\n"; + chatlog_file.flush(); + + chatlog.push_front(tmp); +} + +void Chat::chat_log(CHATSKILL action, ALFONT_FONT * font) { + chat_log(const_msg(action), BY_SERVER, font); +} + + +/** draw first n lines of the list onto a Allegro type bitmap buffer + using Alfont + + BITMAP * bmp -> Allegro type bitmap buffer to draw onto + int n -> number of lines to be drawn + ALFONT_FONT * font -> font to use + + NOTE: + take great care using this, make sure the buffer passed is + empty! ;-) anyway, line wrapping is not supported yet. +*/ +void Chat::chat_draw(BITMAP * bmp, int n, ALFONT_FONT * font) { + int y = 600-35, i = 0; + CHATLOG line; + n -= 1; + + for(iter = chatlog.begin(); iter != chatlog.end(); iter++) { + line = *iter; + y -=11; + + switch(line.own) { + case BY_GM : + alfont_textprintf_aa(bmp, font, 1, y, COLOR_BLUE, "Global announcement: "); + alfont_textprintf_aa(bmp, font, TEXT_GETWIDTH("Global announcement: "), y, COLOR_GREEN, line.text.c_str()); + break; + case BY_PLAYER : + alfont_textprintf_aa(bmp, font, 1, y, COLOR_YELLOW, line.nick.c_str()); + alfont_textprintf_aa(bmp, font, line.width, y, COLOR_WHITE, line.text.c_str()); + break; + case BY_OTHER : + alfont_textprintf_aa(bmp, font, 1, y, COLOR_GREEN, line.nick.c_str()); + alfont_textprintf_aa(bmp, font, line.width, y, COLOR_WHITE, line.text.c_str()); + break; + default : + alfont_textprintf_aa(bmp, font, 1, y, COLOR_LIGHTBLUE, line.text.c_str()); + } + + if(i>=n) + return; + i++; + } +} + +/** determines wether to send a command or an ordinary message, then + contructs packets & sends them + + string nick -> the character's name to display infront + string msg -> the message's text which is to be send. + + NOTE: + the nickname is required by the server, if not specified + the message may not be sent unless a command was intended + which requires another packet to be constructed! you can + achieve this by putting a slash ("/") infront of the + message followed by the command name and the message. + of course all slash-commands need implemented handler- + routines. ;-) + remember, a line starting w/ "@" is not a command that needs + to be parsed rather is sent using the normal chat-packet. + + EXAMPLE: + // for an global announcement /- command + chatlog.chat_send("", "/announce Hello to all logged in users!"); + // for simple message by a user /- message + chatlog.chat_send("Zaeiru", "Hello to all users on the screen!"); +*/ +char * Chat::chat_send(string nick, string msg) { + short len = 0, packid; + char *temp = NULL; + + // prepare command + if(msg.substr(0,1)=="/") { + // global announcement + if(msg.substr(0,IS_ANNOUNCE_LENGTH) == IS_ANNOUNCE) { + msg.erase(0,IS_ANNOUNCE_LENGTH); + packid = 0x0099; + } else { + packid = 0x008c; + len = (short)msg.length()+4; + } + len = (short)msg.length()+4; + // prepare ordinary message + } else { + // temporary hack to make messed-up-keyboard-ppl able to send GM commands + if(msg.substr(0,1)=="#") + msg.replace(0,1,"@"); + // end temp. hack XD + nick += " : "; + nick += msg; + msg = nick; + packid = 0x008c; + len = (short)(nick.length()+msg.length()+3); + } + + // send processed message + temp = new char[len]; + memcpy(temp, msg.c_str(), len); + WFIFOW(0) = net_w_value(packid); + WFIFOW(2) = net_w_value(len+4); + memcpy(WFIFOP(4), temp, len); + WFIFOSET(len+4); + delete temp; + nick = msg = ""; + return ""; +} + +/** PRIVATE : + NOTE: + these usually will be left undocumented coz u can't call them + directly anyway. ;-) +*/ + +/** constructs failed messages for actions */ +string Chat::const_msg(CHATSKILL action) { + string msg; + if(action.success == SKILL_FAILED && action.skill == SKILL_BASIC) { + switch(action.bskill) { + case BSKILL_TRADE : + msg = "Trade failed!"; + break; + case BSKILL_EMOTE : + msg = "Emote failed!"; + break; + case BSKILL_SIT : + msg = "Sit failed!"; + break; + case BSKILL_CREATECHAT : + msg = "Chat creating failed!"; + break; + case BSKILL_JOINPARTY : + msg = "Could not join party!"; + break; + case BSKILL_SHOUT : + msg = "Cannot shout!"; + break; + } + + switch(action.reason) { + case RFAIL_SKILLDEP : + msg += " You have not yet reached a high enough lvl!"; + break; + case RFAIL_INSUFHP : + msg += " Insufficient HP!"; + break; + case RFAIL_INSUFSP : + msg += " Insufficient SP!"; + break; + case RFAIL_NOMEMO : + msg += " You have no memos!"; + break; + case RFAIL_SKILLDELAY : + msg += " You cannot do that right now!"; + break; + case RFAIL_ZENY : + msg += " Seems you need more Zeny... ;-)"; + break; + case RFAIL_WEAPON : + msg += " You cannot use this skill with that kind of weapon!"; + break; + case RFAIL_REDGEM : + msg += " You need another red gem!"; + break; + case RFAIL_BLUEGEM : + msg += " You need another blue gem!"; + break; + case RFAIL_OVERWEIGHT : + msg += " You're carrying to much to do this!"; + break; + default : + msg += " Huh? What's that?"; + break; + } + }else{ + switch(action.skill) { + case SKILL_WARP : + msg = "Warp failed..."; + break; + case SKILL_STEAL : + msg = "Could not steal anything..."; + break; + case SKILL_ENVENOM : + msg = "Poison had no effect..."; + break; + } + } + + return msg; +} + +string const_msg(int own) { + string msg; + return msg; +} + diff --git a/src/gui/chat.h b/src/gui/chat.h new file mode 100644 index 00000000..cfe16beb --- /dev/null +++ b/src/gui/chat.h @@ -0,0 +1,150 @@ +/** + + 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 + +*/ + +#ifndef _CHAT_H +#define _CHAT_H + +#include "../main.h" +#include <list> +#include <string> +#include <fstream> +#include <string> + +using namespace std; + +/** + Simple ChatLog Object v0.5 (i'd say...) + + Bestviewd w/ Bitstream Vera Sans Mono @ 9pt and a tab-width of 2 spaces + + Author: kth5 aka Alexander Baldeck + pipe your questions, suggestions and flames to: kth5@gawab.com + + NOTE: + i documented all functions in their implementation. ;-) +*/ + + +#define BY_GM 0 // those should be self-explanatory =) +#define BY_PLAYER 1 +#define BY_OTHER 2 +#define BY_SERVER 3 + +#define ACT_WHISPER 4 // getting whispered at +#define ACT_IS 5 // equivalent to "/me" in irc + +#define IS_ANNOUNCE "/announce " +#define IS_ANNOUNCE_LENGTH 10 + +/** + gets in between usernick and message text depending on + message type +*/ +#define CAT_NORMAL ": " +#define CAT_IS "" +#define CAT_WHISPER " says: " + +/** some generic color macros */ +#define COLOR_WHITE (makecol(255,255,255)) // plain white +#define COLOR_BLUE (makecol( 97,156,236)) // cold gm blue :P +#define COLOR_YELLOW (makecol(255,246, 98)) // sexy yellow +#define COLOR_GREEN (makecol( 39,197, 39)) // cool green +#define COLOR_RED (makecol(255, 0, 0)) // ack red XD +#define COLOR_LIGHTBLUE (makecol( 83,223,246)) // bright blue + +/** calculate text-width in pixel */ +#define TEXT_GETWIDTH(str) (alfont_text_length(font, str)) + +/** job dependend identifiers (?) */ +#define SKILL_BASIC 0x0001 +#define SKILL_WARP 0x001b +#define SKILL_STEAL 0x0032 +#define SKILL_ENVENOM 0x0034 + +/** basic skills identifiers */ +#define BSKILL_TRADE 0x0000 +#define BSKILL_EMOTE 0x0001 +#define BSKILL_SIT 0x0002 +#define BSKILL_CREATECHAT 0x0003 +#define BSKILL_JOINPARTY 0x0004 +#define BSKILL_SHOUT 0x0005 +#define BSKILL_PK 0x0006 // ?? +#define BSKILL_SETALLIGN 0x0007 // ?? + +/** reasons why action failed */ +#define RFAIL_SKILLDEP 0x00 +#define RFAIL_INSUFHP 0x01 +#define RFAIL_INSUFSP 0x02 +#define RFAIL_NOMEMO 0x03 +#define RFAIL_SKILLDELAY 0x04 +#define RFAIL_ZENY 0x05 +#define RFAIL_WEAPON 0x06 +#define RFAIL_REDGEM 0x07 +#define RFAIL_BLUEGEM 0x08 +#define RFAIL_OVERWEIGHT 0x09 +#define RFAIL_GENERIC 0x0a + +/** should always be zero if failed */ +#define SKILL_FAILED 0x00 + +struct CHATSKILL { + short skill; + short bskill; + short unused; + char success; + char reason; +}; + +class Chat { + public : + Chat(const char *, int); + void chat_dlgrsize(int); + + void chat_log(string, int, ALFONT_FONT *); + void chat_log(CHATSKILL, ALFONT_FONT *); // function overload -> calls original chat_log() + // after processing the packet + + void chat_draw(BITMAP *, int, ALFONT_FONT *); + char * chat_send(string, string); + ~Chat(); + private : + ofstream chatlog_file; + + typedef struct CHATLOG { // list item container object + string nick; + string text; + int own; + int width; + }; + + list<CHATLOG> chatlog; // list object ready to accept out CHATLOG struct :) + list<CHATLOG>::iterator iter; + + int items; + int items_keep; + + string const_msg(CHATSKILL); // contructs action-fail messages + string const_msg(int); // constructs normal messages (not implemented yet) +}; + +#endif diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp new file mode 100644 index 00000000..cb43c47b --- /dev/null +++ b/src/gui/gui.cpp @@ -0,0 +1,1471 @@ +/** + + 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 "gui.h" +#include "allegro/internal/aintern.h" +#include <math.h> +#include <alfont.h> +#include "../Sound/sound.h" + +#ifndef WIN32 +#define __cdecl __attribute__((cdecl)) +#endif + +#define GUI_BMP_COUNT 11 +#define GUI_BMP_OFS_BUTTON 0 +#define GUI_BMP_OFS_SLIDER 4 +#define GUI_BMP_OFS_CHECKBOX 5 +#define GUI_BMP_OFS_RADIOBUTTON 6 +#define GUI_BMP_OFS_TEXTBOX 7 +#define GUI_BMP_OFS_LISTBOX 8 +#define GUI_BMP_OFS_DIALOG 9 + +#define GUI_CALL_BUTTONCALLBACK(d) +static BITMAP *gui__repository[GUI_BMP_COUNT]; + +/* The currently active skin */ +LexSkin gui_skin; +BITMAP *gui_bitmap; +ALFONT_FONT *gui_font; +bool drag; +DATAFILE *gui_gfx; + +extern TmwSound sound; + +/* very internal update stuff */ +int (*gui__external_slider_callback)(void *, int); +int reroute_slider_proc(void *dp3, int d2); + +/** Initialize gui system */ +void init_gui(BITMAP *bitmap, const char *skin) { + gui_bitmap = bitmap; + gui_load_skin(skin); + //alfont_init(); + gui_font = alfont_load_font("./data/skin/arial.ttf"); + alfont_set_font_size(gui_font, 14); + drag = false; + show_mouse(NULL); +} + +int gui_update(DIALOG_PLAYER *player) { + dialog_message(player->dialog, MSG_DRAW, 0, 0); + int ret = update_dialog(player); + draw_sprite(gui_bitmap, mouse_sprite, mouse_x, mouse_y); + return ret; +} + + +void loadButtonSkin() { + char **tokens; + int tokenCount; + int gridx[4]; + int gridy[4]; + int a = 0; + int x,y,mode; + + tokens = get_config_argv("button", "gridx", &tokenCount); + for (a=0; a < 4; a++) { + gridx[a] = atoi(tokens[a]); + } + tokens = get_config_argv("button", "gridy", &tokenCount); + for (a=0; a < 4; a++) { + gridy[a] = atoi(tokens[a]); + } + + tokens = get_config_argv("button", "textcol_norm", &tokenCount); + gui_skin.button.textcolor[0] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("button", "textcol_hilite", &tokenCount); + gui_skin.button.textcolor[1] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("button", "textcol_pressed", &tokenCount); + gui_skin.button.textcolor[2] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("button", "textcol_disabled", &tokenCount); + gui_skin.button.textcolor[3] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + + gui__repository[GUI_BMP_OFS_BUTTON + 0] = (BITMAP *)gui_gfx[0].dat; + gui__repository[GUI_BMP_OFS_BUTTON + 1] = (BITMAP *)gui_gfx[2].dat; + gui__repository[GUI_BMP_OFS_BUTTON + 2] = (BITMAP *)gui_gfx[3].dat; + gui__repository[GUI_BMP_OFS_BUTTON + 3] = (BITMAP *)gui_gfx[1].dat; + + for (mode=0; mode < 4; mode++) { + a=0; + for (y=0; y < 3; y++) { + for (x=0; x < 3; x++) { + gui_skin.button.background[mode].grid[a] = create_sub_bitmap( + gui__repository[GUI_BMP_OFS_BUTTON + mode], + gridx[x] , gridy[y], + gridx[x+1]-gridx[x]+1, gridy[y+1]-gridy[y]+1 + ); + a++; + } + } + } +} + + +void loadSliderSkin() { + int x, y, w, h,o1,o2; + char **tokens; + int tokenCount; + + gui__repository[GUI_BMP_OFS_SLIDER] = (BITMAP *)gui_gfx[8].dat; + + tokens = get_config_argv("slider", "slider_h", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + + tokens = get_config_argv("slider", "slider_h_ofs", &tokenCount); + o1 = atoi(tokens[0]); o2 = atoi(tokens[1]); + + gui_skin.slider.hSlider[0] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x , y, o1-x , h); + gui_skin.slider.hSlider[1] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], o1, y, o2-o1 , h); + gui_skin.slider.hSlider[2] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], o2, y, w-(o2-x), h); + + tokens = get_config_argv("slider", "slider_v", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + + tokens = get_config_argv("slider", "slider_v_ofs", &tokenCount); + o1 = atoi(tokens[0]); o2 = atoi(tokens[1]); + + gui_skin.slider.vSlider[0] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x, y, w, o1 - y); + gui_skin.slider.vSlider[1] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x, o1, w, o2 - o1); + gui_skin.slider.vSlider[2] = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x, o2, w, h - (o2-y)); + + tokens = get_config_argv("slider", "handle_v", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.slider.vGrip = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x, y, w, h); + + tokens = get_config_argv("slider", "handle_h", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.slider.hGrip = create_sub_bitmap(gui__repository[GUI_BMP_OFS_SLIDER], x, y, w, h); +} + +void loadCheckboxSkin() { + int x, y, w,h; + char **tokens; + int tokenCount; + + gui__repository[GUI_BMP_OFS_CHECKBOX] = (BITMAP *)gui_gfx[4].dat; + + + tokens = get_config_argv("checkbox", "normal", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.checkbox.normal = create_sub_bitmap(gui__repository[GUI_BMP_OFS_CHECKBOX], x , y, w, h); + + tokens = get_config_argv("checkbox", "checked", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.checkbox.checked = create_sub_bitmap(gui__repository[GUI_BMP_OFS_CHECKBOX], x , y, w, h); + + tokens = get_config_argv("checkbox", "disabled", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.checkbox.disabled = create_sub_bitmap(gui__repository[GUI_BMP_OFS_CHECKBOX], x , y, w, h); + + tokens = get_config_argv("checkbox", "disabled_check", &tokenCount); + x = atoi(tokens[0]); y = atoi(tokens[1]); + w = atoi(tokens[2]); h = atoi(tokens[3]); + gui_skin.checkbox.disabled_checked = create_sub_bitmap(gui__repository[GUI_BMP_OFS_CHECKBOX], x , y, w, h); + + tokens = get_config_argv("button", "textcol_norm", &tokenCount); + gui_skin.checkbox.textcolor[0] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("button", "textcol_disabled", &tokenCount); + gui_skin.checkbox.textcolor[1] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); +} + +void loadTextboxSkin() { + char **tokens; + int tokenCount; + int gridx[4]; + int gridy[4]; + int a = 0; + int x,y; + + tokens = get_config_argv("textbox", "gridx", &tokenCount); + for (a=0; a < 4; a++) { + gridx[a] = atoi(tokens[a]); + } + tokens = get_config_argv("textbox", "gridy", &tokenCount); + for (a=0; a < 4; a++) { + gridy[a] = atoi(tokens[a]); + } + + tokens = get_config_argv("textbox", "textcol_norm", &tokenCount); + gui_skin.textbox.textcolor[0] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("textbox", "textcol_disabled", &tokenCount); + gui_skin.textbox.textcolor[1] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + + gui__repository[GUI_BMP_OFS_TEXTBOX] = (BITMAP *)gui_gfx[9].dat; + + + + a=0; + for (y=0; y < 3; y++) { + for (x=0; x < 3; x++) { + gui_skin.textbox.bg.grid[a] = create_sub_bitmap( + gui__repository[GUI_BMP_OFS_TEXTBOX], + gridx[x] , gridy[y], + gridx[x+1]-gridx[x]+1, gridy[y+1]-gridy[y]+1 + ); + a++; + } + } +} + +void loadListboxSkin() { + char **tokens; + int tokenCount; + int gridx[4]; + int gridy[4]; + int a = 0; + int x,y; + + tokens = get_config_argv("listbox", "gridx", &tokenCount); + for (a=0; a < 4; a++) { + gridx[a] = atoi(tokens[a]); + } + tokens = get_config_argv("listbox", "gridy", &tokenCount); + for (a=0; a < 4; a++) { + gridy[a] = atoi(tokens[a]); + } + + tokens = get_config_argv("listbox", "textcol_norm", &tokenCount); + gui_skin.listbox.textcolor[0] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("listbox", "textcol_selected", &tokenCount); + gui_skin.listbox.textcolor[1] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("listbox", "textbg_selected", &tokenCount); + gui_skin.listbox.textcolor[2] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + tokens = get_config_argv("listbox", "textcol_disabled", &tokenCount); + gui_skin.listbox.textcolor[3] = makecol(atoi(tokens[0]),atoi(tokens[1]),atoi(tokens[2])); + + gui__repository[GUI_BMP_OFS_LISTBOX+0] = (BITMAP *)gui_gfx[6].dat; + gui__repository[GUI_BMP_OFS_LISTBOX+1] = (BITMAP *)gui_gfx[10].dat; + + a=0; + for (y=0; y < 3; y++) { + for (x=0; x < 3; x++) { + gui_skin.listbox.bg.grid[a] = create_sub_bitmap( + gui__repository[GUI_BMP_OFS_LISTBOX], + gridx[x] , gridy[y], + gridx[x+1]-gridx[x]+1, gridy[y+1]-gridy[y]+1 + ); + a++; + } + } + + tokens = get_config_argv("listbox", "vscroll_gridx", &tokenCount); + for (a=0; a < 4; a++) { + gridx[a] = atoi(tokens[a]); + } + tokens = get_config_argv("listbox", "vscroll_gridy", &tokenCount); + for (a=0; a < 4; a++) { + gridy[a] = atoi(tokens[a]); + } + a=0; + for (y=0; y < 3; y++) { + for (x=0; x < 3; x++) { + gui_skin.listbox.vscroll.grid[a] = create_sub_bitmap( + gui__repository[GUI_BMP_OFS_LISTBOX+1], + gridx[x] , gridy[y], + gridx[x+1]-gridx[x]+1, gridy[y+1]-gridy[y]+1 + ); + a++; + } + } + +} + +void loadDialogSkin() { + char **tokens; + int tokenCount; + int gridx[4]; + int gridy[4]; + int a = 0; + int x,y; + + tokens = get_config_argv("dialog", "gridx", &tokenCount); + for (a=0; a < 4; a++) { + gridx[a] = atoi(tokens[a]); + } + tokens = get_config_argv("dialog", "gridy", &tokenCount); + for (a=0; a < 4; a++) { + gridy[a] = atoi(tokens[a]); + } + + gui__repository[GUI_BMP_OFS_DIALOG] = (BITMAP *)gui_gfx[5].dat; + + a=0; + for (y=0; y < 3; y++) { + for (x=0; x < 3; x++) { + gui_skin.dialog.bg.grid[a] = create_sub_bitmap( + gui__repository[GUI_BMP_OFS_DIALOG], + gridx[x] , gridy[y], + gridx[x+1]-gridx[x]+1, gridy[y+1]-gridy[y]+1 + ); + a++; + } + } +} + +void drawSkinnedRect(BITMAP*dst, LexSkinnedRect *skin, int x, int y,int w, int h) { + + BITMAP **grid = skin->grid; + + int w0 = grid[0]->w; + int w1 = w - grid[0]->w -grid[2]->w; + int w2 = grid[2]->w; + int h0 = grid[0]->h; + int h1 = h - grid[0]->h - grid[6]->h; + int h2 = grid[6]->h; + + int cx,cy; + + cx = x; cy = y; + masked_blit(grid[0], dst, 0, 0, cx, cy,grid[0]->w,grid[0]->h); + cy += h0; + masked_stretch_blit(grid[3], dst, 0, 0, grid[3]->w,grid[3]->h,cx, cy,w0,h1); + cy += h1; + masked_blit(grid[6], dst, 0, 0, cx, cy,grid[6]->w,grid[6]->h); + + cx += w0; + cy = y; + masked_stretch_blit(grid[1], dst, 0, 0, grid[1]->w,grid[1]->h,cx, cy,w1,h0); + cy += h0; + masked_stretch_blit(grid[4], dst, 0, 0, grid[4]->w,grid[4]->h,cx, cy,w1,h1); + cy += h1; + masked_stretch_blit(grid[7], dst, 0, 0, grid[7]->w,grid[7]->h,cx, cy,w1,h2); + + cx += w1; + cy = y; + masked_blit(grid[2], dst, 0, 0, cx, cy,grid[2]->w,grid[2]->h); + cy += h0; + masked_stretch_blit(grid[5], dst, 0, 0, grid[5]->w,grid[5]->h,cx, cy,w2,h1); + cy += h1; + masked_blit(grid[8], dst, 0, 0, cx, cy,grid[8]->w,grid[7]->h); +} + + +int gui_load_skin(const char* skinname) { + gui__external_slider_callback = NULL; + push_config_state(); + set_config_file(skinname); + gui_gfx = load_datafile(get_config_string("skin", "gfx", 0)); + loadButtonSkin(); + loadSliderSkin(); + loadCheckboxSkin(); + loadTextboxSkin(); + loadListboxSkin(); + loadDialogSkin(); + pop_config_state(); + set_mouse_sprite((BITMAP *)gui_gfx[7].dat); + + return TRUE; +} + +void gui_exit() { + //alfont_destroy_font(gui_font); + gui_shutdown(); + //alfont_exit(); +} + +void gui_shutdown(void) { + int a, b; + + /* Button */ + for (a=0; a < 3; a++) { + for (b=0; b < 9 ; b++) { + destroy_bitmap(gui_skin.button.background[a].grid[b]); + } + + } + /* Slider */ + for (a=0; a < 3; a++) { + destroy_bitmap(gui_skin.slider.hSlider[a]); + destroy_bitmap(gui_skin.slider.vSlider[a]); + } + destroy_bitmap(gui_skin.slider.hGrip); + destroy_bitmap(gui_skin.slider.vGrip); + + /* Checkbox */ + destroy_bitmap(gui_skin.checkbox.normal); + destroy_bitmap(gui_skin.checkbox.checked); + destroy_bitmap(gui_skin.checkbox.disabled); + destroy_bitmap(gui_skin.checkbox.disabled_checked); + + /* Radiobutton */ + destroy_bitmap(gui_skin.radiobutton.normal); + destroy_bitmap(gui_skin.radiobutton.checked); + destroy_bitmap(gui_skin.radiobutton.disabled); + destroy_bitmap(gui_skin.radiobutton.disabled_checked); + + for (a=0; a < GUI_BMP_COUNT; a++) { + destroy_bitmap(gui__repository[a]); + } +} + +/** Draw text for gui widgets */ +int gui_text(BITMAP *bmp, AL_CONST char *s, int x, int y, int color, int centre) { + char tmp[1024]; + int hline_pos = -1; + int len = 0; + int in_pos = 0; + int out_pos = 0; + int pix_len, c; + + while (((c = ugetc(s+in_pos)) != 0) && (out_pos<(int)(sizeof(tmp)-ucwidth(0)))) { + if (c == '&') { + in_pos += uwidth(s+in_pos); + c = ugetc(s+in_pos); + if (c == '&') { + out_pos += usetc(tmp+out_pos, '&'); + in_pos += uwidth(s+in_pos); + len++; + } else hline_pos = len; + } else { + out_pos += usetc(tmp+out_pos, c); + in_pos += uwidth(s+in_pos); + len++; + } + } + usetc(tmp+out_pos, 0); + pix_len = alfont_text_length(gui_font, tmp); + + if (centre)x -= pix_len / 2; + if (bmp) { + alfont_textout_aa(bmp, gui_font, tmp, x, y, color); + if (hline_pos >= 0) { + c = ugetat(tmp, hline_pos); + usetat(tmp, hline_pos, 0); + hline_pos = alfont_text_length(gui_font, tmp); + c = usetc(tmp, c); + usetc(tmp+c, 0); + c = alfont_text_length(gui_font, tmp); + hline(bmp, x+hline_pos, y+alfont_text_height(gui_font)-gui_font_baseline, x+hline_pos+c-1, color); + } + } + return pix_len; +} + +int tmw_text_proc(int msg, DIALOG *d, int c) { + if (msg==MSG_DRAW) { + int rtm; + rtm = alfont_text_mode(-1); + gui_text(gui_bitmap, (char *)d->dp, d->x, d->y, d->fg, FALSE); + alfont_text_mode(rtm); + } + return D_O_K; +} + + +int tmw_button_proc(int msg, DIALOG *d, int c) { + + int rtm = 0; + int col = 0; + int ofs = 0; + int ret = D_O_K; + + if (msg == MSG_DRAW) { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, makecol(255,255,255)); + + if (d->flags & D_DISABLED) { + drawSkinnedRect(gui_bitmap, &gui_skin.button.background[3], d->x, d->y, d->w, d->h); + col = gui_skin.button.textcolor[3]; + } else if (d->flags & D_SELECTED) { + drawSkinnedRect(gui_bitmap, &gui_skin.button.background[2], d->x, d->y, d->w, d->h); + col = gui_skin.button.textcolor[2]; + ofs = 1; + } else if (d->flags & D_GOTMOUSE) { + drawSkinnedRect(gui_bitmap, &gui_skin.button.background[1], d->x, d->y, d->w, d->h); + col = gui_skin.button.textcolor[1]; + } else { + drawSkinnedRect(gui_bitmap, &gui_skin.button.background[0], d->x, d->y, d->w, d->h); + col = gui_skin.button.textcolor[0]; + } + rtm = alfont_text_mode(-1); + gui_text(gui_bitmap, (const char *)d->dp, d->x+d->w/2+ofs, d->y+d->h/2-alfont_text_height(gui_font)/2+ofs, col, TRUE); + alfont_text_mode(rtm); + ret = D_O_K; + } else { + ret = d_button_proc(msg,d,c); + } + return ret; +} + +int tmw_slider_proc(int msg, DIALOG *d, int c) { + int w = 0; + int h = 0; + int x,y; + + int ret = D_O_K; + + static int watchdog = 0; + + watchdog++; + if (watchdog == 1) { + gui__external_slider_callback = (int(__cdecl *)(void *, int))d->dp2; + d->dp2 = (void*)reroute_slider_proc; + } + + if (msg == MSG_DRAW) { + if (d->w >= d->h) { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + /* horiz */ + x = d->x; + y = d->y + (d->h- gui_skin.slider.hSlider[0]->h)/2; + masked_blit(gui_skin.slider.hSlider[0], gui_bitmap, 0, 0, x, y, gui_skin.slider.hSlider[0]->w, gui_skin.slider.hSlider[0]->h); + w = d->w -gui_skin.slider.hSlider[0]->w - gui_skin.slider.hSlider[2]->w; + x+= gui_skin.slider.hSlider[0]->w; + + masked_stretch_blit( + gui_skin.slider.hSlider[1], gui_bitmap, + 0, 0, gui_skin.slider.hSlider[1]->w, gui_skin.slider.hSlider[1]->h, + x, y, w, gui_skin.slider.hSlider[1]->h); + + x+=w; + masked_blit(gui_skin.slider.hSlider[2], gui_bitmap, 0, 0, x, y, gui_skin.slider.hSlider[2]->w, gui_skin.slider.hSlider[2]->h); + + x = d->x + ((d->w-gui_skin.slider.hGrip->w) * d->d2)/d->d1; + y = d->y + (d->h - gui_skin.slider.hGrip->h)/2; + masked_blit(gui_skin.slider.hGrip, gui_bitmap, 0, 0, x, y, gui_skin.slider.hGrip->w, gui_skin.slider.hGrip->h); + } else { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + /* vertic */ + x = d->x+ (d->w- gui_skin.slider.vSlider[0]->w)/2; + y = d->y; + masked_blit(gui_skin.slider.vSlider[0], gui_bitmap, 0, 0, x, y, gui_skin.slider.vSlider[0]->w, gui_skin.slider.vSlider[0]->h); + h = d->h - gui_skin.slider.vSlider[0]->h - gui_skin.slider.vSlider[2]->h; + y += gui_skin.slider.vSlider[0]->h; + + masked_stretch_blit( + gui_skin.slider.vSlider[1], gui_bitmap, + 0, 0, gui_skin.slider.vSlider[1]->w, gui_skin.slider.vSlider[1]->h, + x, y, gui_skin.slider.vSlider[1]->w, h); + + y+=h; + masked_blit(gui_skin.slider.vSlider[2], gui_bitmap, 0, 0, x, y, gui_skin.slider.vSlider[2]->w, gui_skin.slider.vSlider[2]->h); + + y = d->y + d->h - (((d->h-gui_skin.slider.vGrip->h) * d->d2)/d->d1)-gui_skin.slider.vGrip->h; + x = d->x + (d->w - gui_skin.slider.vGrip->w)/2; + if (gui_skin.slider.vGrip->w % 2 !=0) { + x++; + } + masked_blit(gui_skin.slider.vGrip, gui_bitmap, 0, 0, x, y, gui_skin.slider.vGrip->w, gui_skin.slider.vGrip->h); + } + //textprintf(gui_bitmap, gui_font,10, 10, makecol(255,255,255), "%i", d->d2); + } else { + ret = d_slider_proc(msg,d,c); + } + if (watchdog == 1) { + d->dp2 = (void*)gui__external_slider_callback; + } + watchdog--; + return ret; +} + +int tmw_check_proc(int msg, DIALOG *d, int c) { + BITMAP *box = NULL; + int x, y; + int tx, ty, l; + int rtm = 0; + int col = 0; + if (msg == MSG_DRAW) { +// rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + if (d->flags & D_SELECTED) { + if (d->flags & D_DISABLED) { + box = gui_skin.checkbox.disabled_checked; + } else { + box = gui_skin.checkbox.checked; + } + } else if (d->flags & D_DISABLED) { + box = gui_skin.checkbox.disabled; + } else { + box = gui_skin.checkbox.normal; + } + + if (d->flags & D_DISABLED) { + col = gui_skin.checkbox.textcolor[1]; + } else { + col = gui_skin.checkbox.textcolor[0]; + } + + if (d->dp != NULL) { + l = gui_strlen((const char *)d->dp); + } else { + l = 0; + } + + if (d->d1 != 0) { + x = d->x; + tx = x + box->w + box->w/2; + } else { + x = d->x + d->w - box->w; + tx = x - box->w/2 - l; + } + y = d->y + (d->h - box->h)/ 2; + ty = d->y + (d->h - alfont_text_height(gui_font)) / 2; + + masked_blit(box, gui_bitmap, 0, 0, x, y, box->w, box->h); + if (d->dp != NULL) { + rtm = alfont_text_mode(-1); + gui_text(gui_bitmap, (const char *)d->dp, tx, ty, col, 0); + alfont_text_mode(rtm); + } + + + } else { + return d_check_proc(msg, d, c); + } + return D_O_K; +} + +int tmw_radio_proc(int msg, DIALOG *d, int c) { + BITMAP *box = NULL; + int x, y; + int tx, ty, l; + int rtm = 0; + int col = 0; + + + if (msg == MSG_DRAW) { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + if (d->flags & D_SELECTED) { + if (d->flags & D_DISABLED) { + box = gui_skin.radiobutton.disabled_checked; + } else { + box = gui_skin.radiobutton.checked; + } + } else if (d->flags & D_DISABLED) { + box = gui_skin.radiobutton.disabled; + } else { + box = gui_skin.radiobutton.normal; + } + + if (d->flags & D_DISABLED) { + col = gui_skin.radiobutton.textcolor[1]; + } else { + col = gui_skin.radiobutton.textcolor[0]; + } + + if (d->dp != NULL) { + l = gui_strlen((const char *)d->dp); + } else { + l = 0; + } + + if (d->d2 != 0) { + x = d->x; + tx = x + box->w + box->w/2; + } else { + x = d->x + d->w - box->w; + tx = x - box->w/2 - l; + } + y = d->y + (d->h - box->h)/ 2; + ty = d->y + (d->h - alfont_text_height(gui_font)) / 2; + + masked_blit(box, gui_bitmap, 0, 0, x, y, box->w, box->h); + if (d->dp != NULL) { + rtm = alfont_text_mode(-1); + gui_text(gui_bitmap, (const char *)d->dp, tx, ty, col, 0); + alfont_text_mode(rtm); + } + + + } else { + return d_radio_proc(msg, d, c); + } + return D_O_K; +} + +int tmw_edit_proc(int msg, DIALOG *d, int c) { +// BITMAP *box = NULL; + int x; + int tx, ty, l; + int rtm = 0; + int col = 0; + char* start; + char* text; + char hack; + int cl, cr, cb, ct; + int lb, rb; + + + if (msg == MSG_DRAW) { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + drawSkinnedRect(gui_bitmap, &gui_skin.textbox.bg, d->x, d->y, d->w, d->h); + + if (d->flags & D_DISABLED) { + col = gui_skin.textbox.textcolor[1]; + } else { + col = gui_skin.textbox.textcolor[0]; + } + + lb = gui_skin.textbox.bg.grid[0]->w; + rb = gui_skin.textbox.bg.grid[2]->w; + tx = d->x + lb; + ty = d->y + (d->h - alfont_text_height(gui_font))/2; + + + text = (char *)d->dp; + start = text; + + rtm = alfont_text_mode(-1); + + if (gui_bitmap->clip) { + cl = gui_bitmap->cl; + ct = gui_bitmap->ct; + cr = gui_bitmap->cr; + cb = gui_bitmap->cb; + } else { + cl=ct=0; + cr=gui_bitmap->w; + cb=gui_bitmap->h; + } + set_clip_rect(gui_bitmap, tx, ty, d->x+d->w-rb, ty + alfont_text_height(gui_font)); // set_clip() is deprecated use set_clip_rect() instead + hack = text[d->d2]; + text[d->d2] = '\0'; + l = alfont_text_length(gui_font, text); + text[d->d2] = hack; + + if (l > d->w-lb-rb) { + tx += ((d->w-lb-rb) - l); + } + gui_text(gui_bitmap, start, tx, ty, col, 0); + + + if (d->flags & D_GOTFOCUS) { + hack = text[d->d2]; + text[d->d2] = '\0'; + x = tx + alfont_text_length(gui_font, text); + vline(gui_bitmap, x, ty, ty + alfont_text_height(gui_font), col); + text[d->d2] = hack; + } + alfont_text_mode(rtm); + set_clip_rect(gui_bitmap, cl, ct, cr, cb); + } else { + return d_edit_proc(msg, d, c); + } + return D_O_K; +} + +int tmw_password_proc(int msg, DIALOG *d, int c) { +// BITMAP *box = NULL; + int x; + int tx, ty, l; + int rtm = 0; + int col = 0; + char* start; + char* text; + char hack; + int cl, cr, cb, ct; + int lb, rb; + + + if (msg == MSG_DRAW) { + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + drawSkinnedRect(gui_bitmap, &gui_skin.textbox.bg, d->x, d->y, d->w, d->h); + + if (d->flags & D_DISABLED) { + col = gui_skin.textbox.textcolor[1]; + } else { + col = gui_skin.textbox.textcolor[0]; + } + + lb = gui_skin.textbox.bg.grid[0]->w; + rb = gui_skin.textbox.bg.grid[2]->w; + tx = d->x + lb; + ty = d->y + (d->h - alfont_text_height(gui_font))/2; + + + text = (char *)malloc(strlen((char *)d->dp)+1); + // Sull: new ansi standard + unsigned int i; + for(i=0;i<strlen((char *)d->dp);i++)text[i] = '*'; + text[i] = '\0'; + start = text; + + rtm = alfont_text_mode(-1); + + if (gui_bitmap->clip) { + cl = gui_bitmap->cl; + ct = gui_bitmap->ct; + cr = gui_bitmap->cr; + cb = gui_bitmap->cb; + } else { + cl=ct=0; + cr=gui_bitmap->w; + cb=gui_bitmap->h; + } + set_clip_rect(gui_bitmap, tx, ty, d->x+d->w-rb, ty + alfont_text_height(gui_font)); + + hack = text[d->d2]; + text[d->d2] = '\0'; + l = alfont_text_length(gui_font, text); + text[d->d2] = hack; + + if (l > d->w-lb-rb) { + tx += ((d->w-lb-rb) - l); + } + gui_text(gui_bitmap, start, tx, ty, col, 0); + + + if (d->flags & D_GOTFOCUS) { + hack = text[d->d2]; + text[d->d2] = '\0'; + x = tx + alfont_text_length(gui_font, text); + vline(gui_bitmap, x, ty, ty + alfont_text_height(gui_font), col); + text[d->d2] = hack; + } + alfont_text_mode(rtm); + set_clip_rect(gui_bitmap, cl, ct, cr, cb); + } else { + return d_edit_proc(msg, d, c); + } + return D_O_K; +} + +int tmw_list_proc(int msg, DIALOG *d, int c) { +// BITMAP *box = NULL; + + static int ignoreRedraw = FALSE; + + int itemCount = 0; + int firstItem = d->d2; + int lastItem = 0; + int selectedItem = d->d1; + int x,y,delta; + int a, col; + int w, h = 0; + int rtm = 0; + int cl, cr, cb, ct; + int th = alfont_text_height(gui_font); + + int vscroll = 0; + int sliderh = 10; + int slidery = 0; + + (*(getfuncptr)d->dp)(-1, &itemCount); + + w = d->w - gui_skin.listbox.bg.grid[0]->w - gui_skin.listbox.bg.grid[2]->w; + h = d->h - gui_skin.listbox.bg.grid[1]->h - gui_skin.listbox.bg.grid[7]->h; + lastItem = MIN(itemCount-1, firstItem + h / alfont_text_height(gui_font)); + + + + if (msg == MSG_DRAW) { + if (ignoreRedraw) { + return D_O_K; + } + rectfill(gui_bitmap, d->x, d->y, d->x + d->w, d->y+d->h, d->bg); + drawSkinnedRect(gui_bitmap, &gui_skin.listbox.bg, d->x, d->y, d->w, d->h); + + (*(getfuncptr)d->dp)(-1, &itemCount); + vscroll = (h/th) < (itemCount-1); + if (vscroll) { + w = d->w - 17 - gui_skin.listbox.bg.grid[0]->w; + drawSkinnedRect(gui_bitmap, &gui_skin.listbox.bg, d->x+d->w-15, d->y+1, 14, d->h-2); + sliderh = MAX(((d->h-2)* (h / th)) / itemCount, gui_skin.listbox.bg.grid[0]->h*2); + slidery = ((d->h-2-sliderh) * firstItem) / (itemCount); + slidery+= d->y+1; + drawSkinnedRect(gui_bitmap, &gui_skin.listbox.vscroll, d->x+d->w-13, slidery, 11, sliderh); + } + + rtm = alfont_text_mode(-1); + if (gui_bitmap->clip) { + cl = gui_bitmap->cl; + ct = gui_bitmap->ct; + cr = gui_bitmap->cr; + cb = gui_bitmap->cb; + } else { + cl=ct=0; + cr=gui_bitmap->w; + cb=gui_bitmap->h; + } + x = d->x + gui_skin.listbox.bg.grid[0]->w; + y = d->y + gui_skin.listbox.bg.grid[0]->h; + set_clip_rect(gui_bitmap, x,y, x+w, y+h); + + + if (d->flags & D_DISABLED) { + col = gui_skin.listbox.textcolor[3]; + for (a=firstItem; a < lastItem; a++) { + alfont_textout_aa(gui_bitmap, gui_font, (*(getfuncptr)d->dp)(a, 0), x, y, col); + y += alfont_text_height(gui_font); + } + } else { + for (a=firstItem; a <= lastItem; a++) { + if (a==d->d1) { + col = gui_skin.listbox.textcolor[1]; + rectfill(gui_bitmap, x, y, x+w, y+alfont_text_height(gui_font)-1, gui_skin.listbox.textcolor[2]); + } else { + col = gui_skin.listbox.textcolor[0]; + } + alfont_textout_aa(gui_bitmap, gui_font, (*(getfuncptr)d->dp)(a, 0), x, y, col); + y += alfont_text_height(gui_font); + } + } + + alfont_text_mode(rtm); + set_clip_rect(gui_bitmap, cl, ct, cr, cb); + + } else if (msg == MSG_CLICK) { + + x = d->x + gui_skin.listbox.bg.grid[0]->w; + y = d->y + gui_skin.listbox.bg.grid[0]->h; + + sliderh = MAX(((d->h-2)* (h / th)) / itemCount, gui_skin.listbox.bg.grid[0]->h*2); + //sliderh = ((d->h-2)* (h / th)) / itemCount; + slidery = ((d->h-2-sliderh) * firstItem) / (itemCount); + slidery+= d->y+1; + + if (mouse_x > (d->x + d->w - 14) && mouse_x < (d->x+d->w-1)) { + // Ok, scroll bar + if (mouse_y >= slidery && mouse_y < slidery + sliderh) { + delta= mouse_y - slidery; + while (mouse_b) { + a = mouse_y - delta - d->y -1; + a *= itemCount; + a /= (d->h-2); + a = MID(0, a, itemCount- h/th); + + if (a != d->d2) { + d->d2 = a; + scare_mouse(); + object_message(d, MSG_DRAW, 0); + unscare_mouse(); + } + + slidery = ((d->h-2) * firstItem) / (itemCount); + slidery+= d->y+1; + } + } else if (mouse_y < slidery) { + a = d->d2 - (h/th)+1; + a = MID(0, a, itemCount- h/th); + + + d->d2 = a; + scare_mouse(); + object_message(d, MSG_DRAW, 0); + unscare_mouse(); + + while (mouse_b) { + } + } else if (mouse_y > slidery + sliderh) { + a = d->d2 + (h/th)-1; + a = MID(0, a, itemCount- h/th); + d->d2 = a; + + scare_mouse(); + object_message(d, MSG_DRAW, 0); + unscare_mouse(); + + while (mouse_b) { + } + } + } else if (mouse_x >= x && mouse_x < x+w && mouse_y >= y && mouse_y < y+h) { + + while (mouse_b) { + a = firstItem + (mouse_y-y) / alfont_text_height(gui_font); + + if (a <= lastItem && a != selectedItem) { + d->d1 = selectedItem = a; + scare_mouse(); + object_message(d, MSG_DRAW, 0); + unscare_mouse(); + } + } + } + } else { + ignoreRedraw = (msg == MSG_GOTFOCUS || msg == MSG_LOSTFOCUS); + a = d_list_proc(msg, d, c); + + if (a == D_USED_CHAR) { + if (d->d1 < d->d2) { + if (d->d1 > 0) { + d->d1 = d->d2; + } + } else if (d->d1 > d->d2 + h/th -1) { + d->d2 = d->d1 - h/th + 1; + } + } + + return a; + } + return D_O_K; +} + +int tmw_dialog_proc(int msg, DIALOG *d, int c) { + int rtm; + int x, y; + + if (msg == MSG_CLICK) { + if(mouse_y < d->y + gui_skin.dialog.bg.grid[1]->h) { + //drag = true; + d->d1 = mouse_x - d->x; + d->d2 = mouse_y - d->y; + } + } else if (msg == MSG_DRAW) { + if((mouse_b & 1)&&(d->d1>=0)&&(d->d2>=0)) {//(drag==true)) { + x = mouse_x-d->d1; + y = mouse_y-d->d2; + if(x<15) { + x=0; + position_mouse(d->d1, mouse_y); + } + if(y<15) { + y=0; + position_mouse(mouse_x, d->d2); + } + if(x+d->w>=785) { + x=800-d->w; + position_mouse(x+d->d1, mouse_y); + } + if(y+d->h>=585) { + y=600-d->h; + position_mouse(mouse_x, y+d->d2); + } + position_dialog(active_dialog, x, y); + } else { + //drag = false; + d->d1 = -1; + d->d2 = -1; + } + drawSkinnedRect(gui_bitmap, &gui_skin.dialog.bg, d->x, d->y, d->w, d->h); + rtm = alfont_text_mode(-1); + alfont_textprintf_centre_aa(gui_bitmap, gui_font, + d->x + d->w/2, + d->y + (gui_skin.dialog.bg.grid[1]->h - alfont_text_height(gui_font))/2, d->fg, "%s", d->dp); + alfont_text_mode(rtm); + } + return D_O_K; +} + +int reroute_slider_proc(void *dp3, int d2) { + int ret = 0; + + if (gui__external_slider_callback != NULL) { + ret = gui__external_slider_callback(dp3, d2); + } + return ret; +} + +// Helper function to draw a scrollable bar +void _gui_draw_scrollable_frame(DIALOG *d, int listsize, int offset, int height, int fg_color, int bg) { + int i, len; + int xx, yy; + + /* create and draw the scrollbar */ + i = ((d->h-5) * height + listsize/2) / listsize; + xx = d->x+d->w-10; + yy = d->y; + + if (offset > 0) { + len = (((d->h-5) * offset) + listsize/2) / listsize; + } else len = 0; + if (yy+i < d->y+d->h-3) { + drawSkinnedRect(gui_bitmap, &gui_skin.listbox.vscroll, xx, yy+len, 10, i); + } else { + drawSkinnedRect(gui_bitmap, &gui_skin.listbox.vscroll, xx, yy, 10, d->h-3); + } +} + +/* _gui_draw_textbox: + * Helper function to draw a textbox object. + */ +void _gui_draw_textbox(char *thetext, int *listsize, int draw, int offset, + int wword, int tabsize, int x, int y, int w, int h, + int disabled, int fore, int deselect, int disable) +{ + int fg = fore; + int y1 = y+4; + int x1; + int len; + int ww = w-6; + char s[16]; + char text[16]; + char space[16]; + char *printed = text; + char *scanned = text; + char *oldscan = text; + char *ignore = NULL; + char *tmp, *ptmp; + int width; + int line = 0; + int i = 0; + int noignore; + int rtm; + + usetc(s+usetc(s, '.'), 0); + usetc(text+usetc(text, ' '), 0); + usetc(space+usetc(space, ' '), 0); + + /* find the correct text */ + if (thetext != NULL) { + printed = thetext; + scanned = thetext; + } + + /* choose the text color */ + if (disabled) + fg = disable; + + rtm = alfont_text_mode(-1); + + /* loop over the entire string */ + while (1) { + width = 0; + + /* find the next break */ + while (ugetc(scanned)) { + /* check for a forced break */ + if (ugetc(scanned) == '\n') { + scanned += uwidth(scanned); + + /* we are done parsing the line end */ + break; + } + + /* the next character length */ + usetc(s+usetc(s, ugetc(scanned)), 0); + len = alfont_text_length(gui_font, s); + + /* modify length if its a tab */ + if (ugetc(s) == '\t') + len = tabsize * alfont_text_length(gui_font, space); + + /* check for the end of a line by excess width of next char */ + if (width+len >= ww) { + /* we have reached end of line do we go back to find start */ + if (wword) { + /* remember where we were */ + oldscan = scanned; + noignore = FALSE; + + /* go backwards looking for start of word */ + while (!uisspace(ugetc(scanned))) { + /* don't wrap too far */ + if (scanned == printed) { + /* the whole line is filled, so stop here */ + tmp = ptmp = scanned; + while (ptmp != oldscan) { + ptmp = tmp; + tmp += uwidth(tmp); + } + scanned = ptmp; + noignore = TRUE; + break; + } + /* look further backwards to wrap */ + tmp = ptmp = printed; + while (tmp < scanned) { + ptmp = tmp; + tmp += uwidth(tmp); + } + scanned = ptmp; + } + /* put the space at the end of the line */ + if (!noignore) { + ignore = scanned; + scanned += uwidth(scanned); + } + else + ignore = NULL; + + /* check for endline at the convenient place */ + if (ugetc(scanned) == '\n') + scanned += uwidth(scanned); + } + /* we are done parsing the line end */ + break; + } + + /* the character can be added */ + scanned += uwidth(scanned); + width += len; + } + + /* check if we are to print it */ + if ((draw) && (line >= offset) && (y1+text_height(font) < (y+h-3))) { + x1 = x+4; + + /* the initial blank bit */ + //rectfill(gui_bitmap, x+2, y1, x1-1, y1+text_height(font), deselect); + + /* print up to the marked character */ + while (printed != scanned) { + /* do special stuff for each charater */ + switch (ugetc(printed)) { + + case '\r': + case '\n': + /* don't print endlines in the text */ + break; + + /* possibly expand the tabs */ + case '\t': + for (i=0; i<tabsize; i++) { + usetc(s+usetc(s, ' '), 0); + alfont_textout_aa(gui_bitmap, gui_font, s, x1, y1, fg); + x1 += alfont_text_length(gui_font, s); + } + break; + + /* print a normal character */ + default: + if (printed != ignore) { + usetc(s+usetc(s, ugetc(printed)), 0); + alfont_textout_aa(gui_bitmap, gui_font, s, x1, y1, fg); + x1 += alfont_text_length(gui_font, s); + } + } + + /* goto the next character */ + printed += uwidth(printed); + } + /* the last blank bit */ + /*if (x1 <= x+w-3) + rectfill(gui_bitmap, x1, y1, x+w-3, y1+alfont_text_height(gui_font)-1, deselect);*/ + + /* print the line end */ + y1 += alfont_text_height(gui_font); + } + printed = scanned; + + /* we have done a line */ + line++; + + /* check if we are at the end of the string */ + if (!ugetc(printed)) { + /* the under blank bit */ + /*if (draw) + rectfill(gui_bitmap, x+1, y1, x+w-3, y+h-1, deselect);*/ + + /* tell how many lines we found */ + *listsize = line; + alfont_text_mode(rtm); + return; + } + } + + alfont_text_mode(rtm); +} + +int tmw_textbox_proc(int msg, DIALOG *d, int c) { + int height, bar, ret = D_O_K; + int start, top, bottom, l; + int used, delta; + int fg_color = (d->flags & D_DISABLED) ? gui_mg_color : d->fg; + + /* calculate the actual height */ + height = (d->h-8) / alfont_text_height(gui_font); + + switch (msg) { + + case MSG_START: + /* measure how many lines of text we contain */ + _gui_draw_textbox((char *)d->dp, &d->d1, + 0, /* DONT DRAW anything */ + d->d2, !(d->flags & D_SELECTED), 8, + d->x, d->y, d->w, d->h, + (d->flags & D_DISABLED), + 0, 0, 0); + break; + + case MSG_DRAW: + /* tell the object to sort of draw, but only calculate the listsize */ + _gui_draw_textbox((char *)d->dp, &d->d1, + 0, /* DONT DRAW anything */ + d->d2, !(d->flags & D_SELECTED), 8, + d->x, d->y, d->w, d->h, + (d->flags & D_DISABLED), + 0, 0, 0); + + if (d->d1 > height) { + bar = 12; + } + else { + bar = 0; + d->d2 = 0; + } + + /* now do the actual drawing */ + _gui_draw_textbox((char *)d->dp, &d->d1, 1, d->d2, + !(d->flags & D_SELECTED), 8, + d->x, d->y, d->w-bar, d->h, + (d->flags & D_DISABLED), + fg_color, d->bg, gui_mg_color); + + /* draw the frame around */ + _gui_draw_scrollable_frame(d, d->d1, d->d2, height, fg_color, d->bg); + + break; + + case MSG_CLICK: + /* figure out if it's on the text or the scrollbar */ + bar = (d->d1 > height); + + if ((!bar) || (gui_mouse_x() < d->x+d->w-13)) { + /* clicked on the text area */ + ret = D_O_K; + } + else { + /* clicked on the scroll area */ + _handle_scrollable_scroll_click(d, d->d1, &d->d2, height); + } + break; + + case MSG_CHAR: + start = d->d2; + used = D_USED_CHAR; + + if (d->d1 > 0) { + if (d->d2 > 0) + top = d->d2+1; + else + top = 0; + + l = (d->h-8)/alfont_text_height(gui_font); + + bottom = d->d2 + l - 1; + if (bottom >= d->d1-1) + bottom = d->d1-1; + else + bottom--; + + if ((c>>8) == KEY_UP) + d->d2--; + else if ((c>>8) == KEY_DOWN) + d->d2++; + else if ((c>>8) == KEY_HOME) + d->d2 = 0; + else if ((c>>8) == KEY_END) + d->d2 = d->d1-l; + else if ((c>>8) == KEY_PGUP) + d->d2 -= (bottom-top) ? bottom-top : 1; + else if ((c>>8) == KEY_PGDN) + d->d2 += (bottom-top) ? bottom-top : 1; + else + used = D_O_K; + + /* make sure that the list stays in bounds */ + if (d->d2 > d->d1-l) + d->d2 = d->d1-l; + if (d->d2 < 0) + d->d2 = 0; + } + else + used = D_O_K; + + /* if we changed something, better redraw... */ + if (d->d2 != start) + d->flags |= D_DIRTY; + + ret = used; + break; + + case MSG_WHEEL: + l = (d->h-8)/alfont_text_height(gui_font); + delta = (l > 3) ? 3 : 1; + + /* scroll, making sure that the list stays in bounds */ + start = d->d2; + d->d2 = (c > 0) ? MAX(0, d->d2-delta) : MIN(d->d1-l, d->d2+delta); + + /* if we changed something, better redraw... */ + if (d->d2 != start) + d->flags |= D_DIRTY; + + ret = D_O_K; + break; + + case MSG_WANTFOCUS: + /* if we don't have a scrollbar we can't do anything with the focus */ + if (d->d1 > height) + ret = D_WANTFOCUS; + break; + + default: + ret = D_O_K; + } + + return ret; +} + +int tmw_bitmap_proc(int msg, DIALOG *d, int c) { + if(msg==MSG_DRAW) { + drawSkinnedRect(gui_bitmap, &gui_skin.textbox.bg, d->x, d->y, d->w, d->h); + if(d->dp!=NULL) + masked_blit(((BITMAP *)d->dp), gui_bitmap, 0, 0, d->x+(d->w-d->d1)/2, d->y+2, d->d1, d->d2); + } + return D_O_K; +} + +void ok(const char *title, const char *message) { +DIALOG alert_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 0, 0, 0, 60, 0, -1, 0, 0, 0, 0, (void *)title, NULL, NULL }, + { tmw_text_proc, 2, 22, 0, 0, 0, 0, 0, 0, 0, 0, (void *)message, NULL, NULL }, + { tmw_button_proc, 0, 40, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char *)"&Ok", NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }}; + + BITMAP *temp = gui_bitmap; + gui_bitmap = screen; + show_mouse(screen); + alert_dialog[0].w = alfont_text_length(gui_font, message)+4; + alert_dialog[1].w = alfont_text_length(gui_font, message); + alert_dialog[1].h = alfont_text_height(gui_font); + alert_dialog[2].x = alfont_text_length(gui_font, message)/2-22; + position_dialog(alert_dialog, 400-alert_dialog[0].w/2, 270); + do_dialog(alert_dialog, 2); + show_mouse(NULL); + gui_bitmap = temp; +} + +unsigned int yes_no(const char *title, const char *message) { + unsigned int ret; + DIALOG alert_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 0, 0, 0, 60, 0, -1, 0, 0, 0, 0, (void *)title, NULL, NULL }, + { tmw_text_proc, 2, 22, 0, 0, 0, 0, 0, 0, 0, 0, (void *)message, NULL, NULL }, + { tmw_button_proc, 0, 40, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char *)"&Yes", NULL, NULL }, + { tmw_button_proc, 0, 40, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char *)"&No", NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }}; + + BITMAP *temp = gui_bitmap; + gui_bitmap = screen; + show_mouse(screen); + int width = alfont_text_length(gui_font, message)+4; + if(width<100)width=100; + alert_dialog[0].w = width; + alert_dialog[1].w = alfont_text_length(gui_font, message); + alert_dialog[1].h = alfont_text_height(gui_font); + alert_dialog[2].x = width/2-46; + alert_dialog[2].x = width/2+2; + position_dialog(alert_dialog, 400-width/2, 270); + ret = do_dialog(alert_dialog, 3); + show_mouse(NULL); + gui_bitmap = temp; + return ret-2; +} diff --git a/src/gui/gui.h b/src/gui/gui.h new file mode 100644 index 00000000..36973b65 --- /dev/null +++ b/src/gui/gui.h @@ -0,0 +1,121 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _TMW_GUI +#define _TMW_GUI + +#include <allegro.h> +#include <alfont.h> +#include <string.h> + +typedef struct { + BITMAP *grid[9]; +} LexSkinnedRect; + +typedef struct { + LexSkinnedRect background[4]; + int textcolor[4]; +} LexButton; + +typedef struct { + BITMAP *hSlider[3]; + BITMAP *vSlider[3]; + BITMAP *hGrip; + BITMAP *vGrip; +} LexSlider; + +typedef struct { + BITMAP *normal; + BITMAP *checked; + BITMAP *disabled; + BITMAP *disabled_checked; + int textcolor[2]; +} LexCheckbox; + +typedef struct { + BITMAP *normal; + BITMAP *checked; + BITMAP *disabled; + BITMAP *disabled_checked; + int textcolor[2]; +} LexRadiobutton; + +typedef struct { + LexSkinnedRect bg; + int textcolor[2]; +} LexTextbox; + +typedef struct { + LexSkinnedRect bg; + LexSkinnedRect vscroll; + int textcolor[4]; +} LexListbox; + +typedef struct { + LexSkinnedRect bg; +} LexDialog; + +typedef struct { + LexButton button; + LexSlider slider; + LexCheckbox checkbox; + LexRadiobutton radiobutton; + LexTextbox textbox; + LexListbox listbox; + LexDialog dialog; +} LexSkin; + +extern LexSkin gui_skin; +extern BITMAP *gui_bitmap; +extern ALFONT_FONT *gui_font; + +/* Definition of the callback function prototypes */ +typedef int (*gui_buttonCallback)(int id); +typedef char *(*getfuncptr)(int, int *); + +void init_gui(BITMAP *dest_bitmap, const char *skin); +void gui_exit(); +int gui_update(DIALOG_PLAYER *player); +int gui_load_skin(const char* skinname); +void gui_shutdown(void); + +int tmw_button_proc(int msg, DIALOG *d, int c); +int tmw_slider_proc(int msg, DIALOG *d, int c); +int tmw_check_proc(int msg, DIALOG *d, int c); +int tmw_radio_proc(int msg, DIALOG *d, int c); +int tmw_edit_proc(int msg, DIALOG *d, int c); +int tmw_password_proc(int msg, DIALOG *d, int c); +int tmw_list_proc(int msg, DIALOG *d, int c); +int tmw_text_proc(int msg, DIALOG *d, int c); +int tmw_dialog_proc(int msg, DIALOG *d, int c); +int tmw_textbox_proc(int msg, DIALOG *d, int c); +int tmw_bitmap_proc(int msg, DIALOG *d, int c); + +void ok(const char *title, const char *message); +unsigned int yes_no(const char *title, const char *message); + +#endif diff --git a/src/gui/inventory.cpp b/src/gui/inventory.cpp new file mode 100644 index 00000000..72c2cbaa --- /dev/null +++ b/src/gui/inventory.cpp @@ -0,0 +1,249 @@ +/** + + 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 "inventory.h" + +void TmwInventory::create(int tempxpos, int tempypos) { + xpos =tempxpos; + ypos =tempypos; + + itemPIC[0].pic = load_bitmap("items/herb.bmp", NULL); //ID=0 + itemPIC[1].pic = load_bitmap("items/magicthing.bmp", NULL); //ID=1 + empty = load_bitmap("items/empty.bmp", NULL); + selected = load_bitmap("items/selected.bmp", NULL); + + for(int i = 0; i< 10; i++) { + for(int ii = 0; ii< 10; ii++) { + items[i][ii].flag = 0; //doesn't hold anything + items[i][ii].itemIDNum = -1; //doesn't exist :) + items[i][ii].xpos = empty->w*i+1; + items[i][ii].ypos = empty->h*ii; + items[i][ii].num = 0; + } + } + + //create two fake items + items[0][0].flag = 1; + items[0][0].itemIDNum = 0; + items[0][0].num = 1; + + items[2][0].flag = 1; + items[2][0].itemIDNum = 1; + items[2][0].num = 3; + + backgroundSmall = create_bitmap(empty->w*10+10, empty->h+10); + backgroundBig = create_bitmap(empty->w*10+10, empty->h*10+10); + title = create_bitmap(15, backgroundSmall->h); + floodfill(title,0,0,200); + floodfill(backgroundSmall,0,0,100); + floodfill(backgroundBig,0,0,100); + areDisplaying = 0; + dragingWindow = 0; + lastSelectedX = -1; + lastSelectedY = -1; + bigwindow = 0; //false +} + +void TmwInventory::draw(BITMAP * buffer) { + // let's draw the inventory + if(areDisplaying) { + int max; + if(!bigwindow) { + max = 1; + blit(backgroundSmall, buffer, 0, 0, xpos-5, ypos-5, 800, 600); + } else { + max = 10; + blit(backgroundBig, buffer, 0, 0, xpos-5, ypos-5, 800, 600); + } + blit(title, buffer, 0, 0, xpos+backgroundSmall->w-5, ypos+-5, 800, 600); + for(int itemX = 0; itemX< 10; itemX++) { + for(int itemY = 0;itemY<max;itemY++) { + int draw = 0; + + blit(empty, buffer, 0, 0, (xpos+items[itemX][itemY].xpos), (ypos+items[itemX][itemY].ypos), 800, 600); + + if(mouse_b&1) { + if(xpos+items[itemX][itemY].xpos+empty->w > mouse_x && xpos+items[itemX][itemY].xpos < mouse_x) + if(ypos+items[itemX][itemY].ypos+empty->h > mouse_y && ypos+items[itemX][itemY].ypos < mouse_y) { + //selected + masked_blit(selected, buffer, 0, 0, (xpos+items[itemX][itemY].xpos), (ypos+items[itemX][itemY].ypos), 800, 600); + draw = 1; + if(items[itemX][itemY].flag) // have a item + if(!dragingItem) { //not dragging it + dragingItem=1; //begin to drag + ghostOldIDX = itemX; + ghostOldIDY = itemY; + ghostID = items[itemX][itemY].itemIDNum; + ghostX = mouse_x; + ghostY = mouse_y; + } + } + } else { + if(lastSelectedX != -1 && dragingItem) { // have stoped dragging it over a itemholder + //swap place + itemHolder temp; + int txpos1,typos1,txpos2,typos2; + txpos1 = items[lastSelectedX][lastSelectedY].xpos; + typos1 = items[lastSelectedX][lastSelectedY].ypos; + txpos2 = items[ghostOldIDX][ghostOldIDY].xpos; + typos2 = items[ghostOldIDX][ghostOldIDY].ypos; + temp = items[lastSelectedX][lastSelectedY]; + items[lastSelectedX][lastSelectedY] = items[ghostOldIDX][ghostOldIDY]; + items[ghostOldIDX][ghostOldIDY] = temp; + items[lastSelectedX][lastSelectedY].xpos = txpos1; + items[lastSelectedX][lastSelectedY].ypos = typos1; + items[ghostOldIDX][ghostOldIDY].xpos = txpos2; + items[ghostOldIDX][ghostOldIDY].ypos = typos2; + } + dragingItem = 0; // stop dragging + } + + if(xpos+items[itemX][itemY].xpos+empty->w > mouse_x && xpos+items[itemX][itemY].xpos < mouse_x && ypos+items[itemX][itemY].ypos+empty->h > mouse_y && ypos+items[itemX][itemY].ypos < mouse_y ) { + // a hoover + lastSelectedX = itemX; + lastSelectedY = itemY; + } + + if(items[itemX][itemY].flag) //draw the item + masked_blit(itemPIC[items[itemX][itemY].itemIDNum].pic, buffer, 0, 0, (xpos+items[itemX][itemY].xpos), (ypos+items[itemX][itemY].ypos), 800, 600); + + //the number of that item + if(!bigwindow) + alfont_textprintf_aa(buffer, gui_font, xpos+items[itemX][itemY].xpos+20, ypos+items[itemX][itemY].ypos+empty->h-15, makecol(0,0,0), "%i",items[itemX][itemY].num); + else + alfont_textprintf_aa(buffer, gui_font, xpos+items[itemX][itemY].xpos+20, ypos+items[itemX][itemY].ypos+empty->h-15, makecol(0,0,0), "%i",items[itemX][itemY].num); + } + } + + + if(mouse_b&2) { + if(xpos+title->w+backgroundSmall->w > mouse_x && xpos+backgroundSmall->w < mouse_x) + if(ypos+title->h > mouse_y && ypos < mouse_y) { + if(bigwindow) + bigwindow=0; + else + bigwindow = 1; + } + } + } + + if(mouse_b&1) { + if(xpos+title->w+backgroundSmall->w > mouse_x && xpos+backgroundSmall->w < mouse_x) + if(ypos+title->h > mouse_y && ypos < mouse_y) { //begin to move the window + xpos = mouse_x-(backgroundSmall->w); + ypos = mouse_y; + dragingWindow=1; + } + } else { dragingWindow=0;} + + if(dragingWindow) { //moving the window ? + xpos = mouse_x-(backgroundSmall->w); + ypos = mouse_y; + } + + if(dragingItem) { //moving the item + masked_blit(itemPIC[ghostID].pic, buffer, 0, 0, ghostX, ghostY, 800, 600); + ghostX = mouse_x; + ghostY = mouse_y; + } +} + + +void TmwInventory::show(int val) { + if(val) + areDisplaying = 1; + + if(!val) + areDisplaying = 0; +} + +int TmwInventory::addItem(int idnum, int antal) +{ +int found, tempi, tempii; +found = 0; +tempi = -1; +for(int i = 0; i< 10; i++) + { + for(int ii = 0; ii< 10; ii++) + { + if(items[i][ii].itemIDNum == idnum) + { found = 1; items[i][ii].num = antal; return -2; } + } + } +if(!found) +{ +for(int ii = 0; ii< 10; ii++) + { + for(int i = 0; i< 10; i++) + { + if(items[i][ii].flag == 0) + {tempi = i; tempii = ii; ii=10;i=10;} + } + } + if(tempi != -1) + { + items[tempi][tempii].flag = 1; + items[tempi][tempii].itemIDNum = idnum; + items[tempi][tempii].num = antal; + return 1; + } else { return -1; } + +} +return -3; +} + +int TmwInventory::rmItem(int idnum) +{ +int found, tempi; +found = 0; +tempi = -1; +for(int i = 0; i< 10; i++) + { + for(int ii = 0; ii< 10; ii++) + { + if(items[i][ii].itemIDNum == idnum) + { items[i][ii].itemIDNum = -1; + items[i][ii].flag = 0; + items[i][ii].num = 0; + return 1; + } + } + } +return -1; +} + +int TmwInventory::changeNum(int idnum, int antal) +{ +int found, tempi; +found = 0; +tempi = -1; +for(int i = 0; i< 10; i++) + { + for(int ii = 0; ii< 10; ii++) + { + if(items[i][ii].itemIDNum == idnum) + { items[i][ii].num = antal; return 1; } + } + } +return -1; +}
\ No newline at end of file diff --git a/src/gui/inventory.h b/src/gui/inventory.h new file mode 100644 index 00000000..75939950 --- /dev/null +++ b/src/gui/inventory.h @@ -0,0 +1,80 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _INVENTORY_H +#define _INVENTORY_H +#include <allegro.h> +#ifdef WIN32 +#include <winalleg.h> +#endif +#include "../main.h" +#include "../game.h" +#include "../log.h" +#include "../Net/network.h" +#include "gui.h" + +struct itemHolder { // the holder of a item + int flag; // don't really know why I use this :) + int itemIDNum; // the id of the item + int num; // number of items + int xpos,ypos; // where am I? +}; + +struct itemID {//the holder of the pictures for each item, maybe more in the future + BITMAP * pic; +}; + +class TmwInventory{ + public: + TmwInventory() {}; + ~TmwInventory() {}; + + void create(int tempxpos, int tempypos); // create the window + void draw(BITMAP *); // draw the window (if areDisplaying != 0 ) + void show(int val); // choose between the show and don't show the window + void toggle() { if(areDisplaying){show(0);}else{show(1);} } + //API + int addItem(int idnum, int antal); //add a item + int rmItem(int idnum); //remove a item + int changeNum(int idnum, int antal); // change nummber of a item + //END API + private: + BITMAP * backgroundSmall; + BITMAP * backgroundBig; + BITMAP * title; + BITMAP * empty; + BITMAP * selected; + itemHolder items[10][10]; // this is the test holder of items + itemID itemPIC[2]; // I only got two items + int ghostX, ghostY, ghostID, ghostOldIDX,ghostOldIDY; //info needed when moving item + int dragingItem, lastSelectedX,lastSelectedY; //info needed when moving item + int areDisplaying, dragingWindow; + int bigwindow; + int xpos, ypos; // Where am I ? +}; + +#endif diff --git a/src/gui/login.cpp b/src/gui/login.cpp new file mode 100644 index 00000000..e3cb6195 --- /dev/null +++ b/src/gui/login.cpp @@ -0,0 +1,139 @@ +/** + + 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 "login.h" +#include "../Graphic/graphic.h" + +/** Display login GUI */ +void login() { +DIALOG login_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 300, 252, 200, 96, 0, -1, 0, 0, 0, 0, (char*)"Login", NULL, NULL }, + { tmw_text_proc, 304, 284, 50, 10, 0, 0, 0, 0, 0, 0, (char*)"Name:", NULL, NULL }, + { tmw_text_proc, 304, 304, 50, 10, 0, 0, 0, 0, 0, 0,(char*)"Password:", NULL, NULL }, + { tmw_edit_proc, 360, 280, 130, 18, 0, -1, 0, 0, 24, 0, username, NULL, NULL }, + { tmw_password_proc, 360, 300, 130, 18, 0, -1, 0, 0, 24, 0, password, NULL, NULL }, + { tmw_button_proc, 398, 322, 44, 18, 0, -1, 'o', D_EXIT, -1, 0, (char*)"&Ok", NULL, NULL }, + { tmw_button_proc, 446, 322, 44, 18, 0, -1, 'c', D_EXIT, -1, 0, (char*)"&Cancel", NULL, NULL }, + { tmw_check_proc, 304, 322, 60, 18, 0, 0, '1', 0, 0, 0, (char*)"keep", NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }, +}; + + if(get_config_int("login", "remember", 0)!=0) { + login_dialog[7].flags = D_SELECTED; + if(get_config_string("login", "username", 0))strcpy(username, get_config_string("login", "username", 0)); + else strcpy(username, "player\0"); + } + centre_dialog(login_dialog); + DIALOG_PLAYER *player = init_dialog(login_dialog, -1); + int gui_exit = 1; + while ((!key[KEY_ESC])&&(gui_exit)&&(state!=EXIT)&&(!key[KEY_ENTER])) { + clear_bitmap(buffer); + if(stretch_mode!=0)blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, 80, 60, 640, 480); + else blit((BITMAP *)graphic[LOGIN_BMP].dat, buffer, 0, 0, -120, -90, 640, 480); + + gui_exit = gui_update(player); + blit(buffer, screen, 0, 0, 0, 0, 800, 600); + } + state = EXIT; + set_config_int("login", "remember", (login_dialog[7].flags & D_SELECTED)>>1); + if(login_dialog[7].flags & D_SELECTED) { + if(!username)strcpy(username, "player\0"); + set_config_string("login", "username", username); + } else set_config_string("login", "username", "player\0"); + log("Player", "username", username); + gui_exit = shutdown_dialog(player); + if((gui_exit==5)||(key[KEY_ENTER])) { + if(username[0]=='\0') { + ok("Error", "Enter your username first"); + warning("Enter your username first"); + state = LOGIN; + } else { + server_login(); + close_session(); + } + } +} + +/** Attempt to login to login server */ +void server_login() { + int ret; + + // Connect to login server + ret = open_session(get_config_string("server", "host", 0), get_config_int("server", "port", 0)); + if(ret==SOCKET_ERROR) { + state = LOGIN; + ok("Error", "Unable to connect to login server"); + warning("Unable to connect to login server"); + return; + } + + // Send login infos + + + WFIFOW(0) = net_w_value(0x0064); + + WFIFOL(2) = 0; + memcpy(WFIFOP(6), username, 24); + memcpy(WFIFOP(30), password, 24); + WFIFOB(54) = 0; + WFIFOSET(55); + + while((in_size<23)||(out_size>0))flush(); + log_hex("Login_Packet", "Packet_ID", RFIFOW(0)); + log_int("Login_Packet", "Packet_length", get_packet_length(RFIFOW(0))); + + if(RFIFOW(0)==0x0069) { + while(in_size<RFIFOW(2))flush(); + n_server = (RFIFOW(2)-47)/32; + server_info = (SERVER_INFO *)malloc(sizeof(SERVER_INFO)*n_server); + account_ID = RFIFOL(8); + session_ID1 = RFIFOL(4); + session_ID2 = RFIFOL(12); + sex = RFIFOB(46); + for(int i=0;i<n_server;i++) { + server_info[i].address = RFIFOL(47+32*i); + memcpy(server_info[i].name, RFIFOP(47+32*i+6), 20); + server_info[i].online_users = RFIFOW(47+32*i+26); + server_info[i].port = RFIFOW(47+32*i+4); + state = CHAR_SERVER; + } + log("Login_Packet", "server_address", iptostring(server_info[0].address)); + log("Login_Packet", "server_name", server_info[0].name); + log_int("Login_Packet", "server_users", server_info[0].online_users); + log_int("Login_Packet", "server_port", server_info[0].port); + RFIFOSKIP(RFIFOW(2)); + } else if(RFIFOW(0)==0x006a) { + switch(RFIFOB(2)) { + case 0: + ok("Error", "Unregistered ID"); + break; + case 1: + ok("Error", "Wrong password"); + break; + } + state = LOGIN; + RFIFOSKIP(23); + } else ok("Error", "Unknown error"); + // Todo: add other packets, also encrypted +} diff --git a/src/gui/login.h b/src/gui/login.h new file mode 100644 index 00000000..c181e0eb --- /dev/null +++ b/src/gui/login.h @@ -0,0 +1,43 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _LOGIN_H +#define _LOGIN_H + +#include <allegro.h> +#ifdef WIN32 +#include <winalleg.h> +#endif +#include "../main.h" +#include "../log.h" +#include "../Net/network.h" +#include "gui.h" + +void login(); +void server_login(); + +#endif diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp new file mode 100644 index 00000000..f1ccdcc2 --- /dev/null +++ b/src/gui/skill.cpp @@ -0,0 +1,56 @@ +/** + + 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 + + By ElvenProgrammer aka Eugenio Favalli (umperio@users.sourceforge.net) + +*/ + +#include "skill.h" + +extern CHAR_SEL_INFO *char_info; + +char str_string[8]; +char agi_string[8]; +char vit_string[8]; +char int_string[8]; +char dex_string[8]; +char luk_string[8]; + +DIALOG skill_dialog[] = { + /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ + { tmw_dialog_proc, 300, 200, 120, 60, 0, 0, 0, 0, 0, 0, (char *)"Skill", NULL, NULL }, + { tmw_text_proc, 304, 224, 252, 100, 0, 0, 0, 0, 0, 0, str_string, NULL, NULL }, + { tmw_text_proc, 304, 234, 252, 100, 0, 0, 0, 0, 0, 0, agi_string, NULL, NULL }, + { tmw_text_proc, 304, 244, 252, 100, 0, 0, 0, 0, 0, 0, vit_string, NULL, NULL }, + { tmw_text_proc, 364, 224, 252, 100, 0, 0, 0, 0, 0, 0, int_string, NULL, NULL }, + { tmw_text_proc, 364, 234, 252, 100, 0, 0, 0, 0, 0, 0, dex_string, NULL, NULL }, + { tmw_text_proc, 364, 244, 252, 100, 0, 0, 0, 0, 0, 0, luk_string, NULL, NULL }, + { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } +}; + +void update_skill_dialog() { + sprintf(str_string, "STR: %i", char_info->STR); + sprintf(agi_string, "AGI: %i", char_info->AGI); + sprintf(vit_string, "VIT: %i", char_info->VIT); + sprintf(int_string, "INT: %i", char_info->INT); + sprintf(dex_string, "DEX: %i", char_info->DEX); + sprintf(luk_string, "LUK: %i", char_info->LUK); +} diff --git a/src/gui/skill.h b/src/gui/skill.h new file mode 100644 index 00000000..a0ce031d --- /dev/null +++ b/src/gui/skill.h @@ -0,0 +1,40 @@ +/** + + 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 + + By ElvenProgrammer aka Eugenio Favalli (umperio@users.sourceforge.net) + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _SKILL_H +#define _SKILL_H + +#include <allegro.h> +#include "../main.h" + +//DIALOG skill_dialog[]; + +void update_skill_dialog(); + +#endif diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 00000000..785418cc --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,189 @@ +/** + + 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 "log.h" + +int warning_n; // Keep warning number + +#define LOG_FILE "The Mana World.log" +#define LOG_VERSION "0.3" + +void init_log() { +#ifdef WIN32 + remove(LOG_FILE); +#else + unlink(LOG_FILE); +#endif + + push_config_state(); + set_config_file(LOG_FILE); +#ifdef WIN32 // for the moment i cant find a valuable unix function for _strdate // Sull + char date[9]; + set_config_string("Core", "date", _strdate(date)); +#endif + set_config_string("Core", "log_version", LOG_VERSION); + set_config_string("Core", "Allegro_ID", allegro_id); + switch(os_type) { + case OSTYPE_UNKNOWN: + set_config_string("Core", "Os", "unknown, or regular MSDOS"); + break; + case OSTYPE_WIN3: + set_config_string("Core", "Os", "Windows 3.1 or earlier"); + break; + case OSTYPE_WIN95: + set_config_string("Core", "Os", "Windows 95"); + break; + case OSTYPE_WIN98: + set_config_string("Core", "Os", "Windows 98"); + break; + case OSTYPE_WINME: + set_config_string("Core", "Os", "Windows ME"); + break; + case OSTYPE_WINNT: + set_config_string("Core", "Os", "Windows NT"); + break; + case OSTYPE_WIN2000: + set_config_string("Core", "Os", "Windows 2000"); + break; + case OSTYPE_WINXP: + set_config_string("Core", "Os", "Windows XP"); + break; + case OSTYPE_OS2: + set_config_string("Core", "Os", "OS/2"); + break; + case OSTYPE_WARP: + set_config_string("Core", "Os", "OS/2 Warp 3"); + break; + case OSTYPE_DOSEMU: + set_config_string("Core", "Os", "Linux DOSEMU"); + break; + case OSTYPE_OPENDOS: + set_config_string("Core", "Os", "Caldera OpenDOS"); + break; + case OSTYPE_LINUX: + set_config_string("Core", "Os", "Linux"); + break; + case OSTYPE_SUNOS: + set_config_string("Core", "Os", "SunOS/Solaris"); + break; + case OSTYPE_FREEBSD: + set_config_string("Core", "Os", "FreeBSD"); + break; + case OSTYPE_NETBSD: + set_config_string("Core", "Os", "NetBSD"); + break; + case OSTYPE_IRIX: + set_config_string("Core", "Os", "IRIX"); + break; + case OSTYPE_QNX: + set_config_string("Core", "Os", "QNX"); + break; + case OSTYPE_UNIX: + set_config_string("Core", "Os", "Unknown Unix variant"); + break; + case OSTYPE_BEOS: + set_config_string("Core", "Os", "BeOS"); + break; + case OSTYPE_MACOS: + set_config_string("Core", "Os", "MacOS"); + break; + default: + set_config_string("Core", "Os", "Unknown"); + break; + } + set_config_int("Core", "Os_version", os_version); + set_config_int("Core", "Os_revision", os_revision); + if(os_multitasking)set_config_string("Core", "Multitasking", "TRUE"); + else set_config_string("Core", "Multitasking", "FALSE"); + + set_config_string("Core", "CPU_Vendor", cpu_vendor); + switch(cpu_family) { + case 3: + set_config_string("Core", "CPU_Family", "386"); + break; + case 4: + set_config_string("Core", "CPU_Family", "486"); + break; + case 5: + set_config_string("Core", "CPU_Family", "Pentium"); + break; + case 6: + set_config_string("Core", "CPU_Family", "Pentium II/III/PRO/Athlon"); + break; + case 15: + set_config_string("Core", "CPU_Family", "Pentium IV"); + break; + default: + set_config_string("Core", "CPU_Family", "Unknown"); + set_config_int("Core", "CPU_Family_ID", cpu_family); + break; + } + + set_config_int("Core", "CPU_model", cpu_model); + + set_config_int("Core", "CPU_capabilities", cpu_capabilities); + pop_config_state(); + + warning_n = 0; +} + +void log(const char *log_section, const char *log_name, const char *log_text) { + push_config_state(); + set_config_file(LOG_FILE); + set_config_string(log_section, log_name, log_text); + pop_config_state(); +} + +void log_hex(const char *log_section, const char *log_name, const short log_value) { + push_config_state(); + set_config_file(LOG_FILE); + set_config_hex(log_section, log_name, log_value); + pop_config_state(); +} + +void log_int(const char *log_section, const char *log_name, const int log_value) { + push_config_state(); + set_config_file(LOG_FILE); + set_config_int(log_section, log_name, log_value); + pop_config_state(); +} + +void error(const char *error_text) { + log("Error", "Last_error", error_text); +#ifdef WIN32 + MessageBox(NULL, error_text, "Error", MB_ICONERROR|MB_OK); +#else + printf("Error: %s", error_text); +#endif + exit(1); +} + +void warning(const char *warning_text) { + char warning_name[40]; + sprintf(warning_name, "warning_%i", warning_n); + log("Error", warning_name, warning_text); +} + +void status(const char *status_text) { + log("Status", "last_function", status_text); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 00000000..5c53e5ca --- /dev/null +++ b/src/log.h @@ -0,0 +1,45 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _LOG_H +#define _LOG_H + +#include <allegro.h> +#ifdef WIN32 +#include <winalleg.h> +#endif +#include <stdio.h> + +void init_log(); +void log(const char *log_section, const char *log_name, const char *log_text); +void log_hex(const char *log_section, const char *log_name, const short log_value); +void log_int(const char *log_section, const char *log_name, const int log_value); +void error(const char *error_text); +void warning(const char *warning_text); +void status(const char *status_text); + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..937c40f6 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,156 @@ +/** + + 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 "./Sound/sound.h" +#include "./Graphic/graphic.h" + +/* Account infos */ +int account_ID, session_ID1, session_ID2; +char sex, n_server, n_character; +SERVER_INFO *server_info; +CHAR_SEL_INFO *char_info; + +BITMAP *playerset; +DATAFILE *graphic, *emotions; + +char username[25]; +char password[25]; +int map_address, char_ID; +short map_port; +char map_name[16]; +unsigned char state; +unsigned short x, y; +unsigned char direction; +unsigned short job, hair, hair_color; +unsigned char stretch_mode; + +// new sound-engine /- kth5 +TmwSound sound; + +void request_exit() { + state = EXIT; +} + +/** Do all initialization stuff */ +void init_engine() { + allegro_init(); + init_log(); + set_close_button_callback(request_exit); // we should not use set_window_close_hook() since it's deprecated and might be gone in the future /-kth5 + set_config_file("tmw.ini"); + #ifdef MACOSX + set_color_depth(32); + Init_SuperEagle(32); + #else + set_color_depth(16); + Init_SuperEagle(16); + #endif + stretch_mode = get_config_int("settings", "stretch", 0); + if(stretch_mode==0) { + if(set_gfx_mode(get_config_int("settings", "screen", 0), 400, 300, 0, 0)) + error(allegro_error); + } else { + if(set_gfx_mode(get_config_int("settings", "screen", 0), 800, 600, 0, 0)) + error(allegro_error); + } + if(install_keyboard()) + error("Unable to install keyboard"); + if(install_timer()) + error("Unable to install timer"); + if(install_mouse()==-1) + error("Unable to install mouse"); + if(alfont_init()!=ALFONT_OK) + error("Unable to install AllegroFont"); + + buffer = create_bitmap(800, 600); + if(!buffer) + error("Not enough memory to create buffer"); + graphic = load_datafile("./data/graphic/graphic.dat"); + if(graphic==NULL) + error("Unable to load graphic datafile"); + playerset = (BITMAP*)graphic[PLAYERSET_BMP].dat; + emotions = load_datafile("./data/graphic/emotions.dat"); + if(emotions==NULL) + error("Unable to load emotions datafile"); + + init_gui(buffer, "./data/skin/aqua.skin"); + + state = LOGIN; +} + +/** Clear the engine */ +void exit_engine() { + gui_exit(); + //alfont_exit(); + destroy_bitmap(buffer); + allegro_exit(); +} + +/** Main */ +int main() { + init_engine(); + + // initialize sound-engine and start playing intro-theme /-kth5 + try{ + if(get_config_int("settings", "sound", 0)==1) + sound.Init(32,20); // inits the sound-subsystem w/ 32 voices / 20 for mod + sound.SetVol(128,128,128); // sets intial volume parameters + //#ifdef WIN32 + //sound.StartMIDI("Sound/Midis/city.mid",-1); // play a midi file + //#endif + }catch(const char * err){ // catch errors and show appropriate messages on-screen (elven plz... ^^) + ok("Sound Engine", err); + warning(err); + } + + while(state!=EXIT) { + switch(state) { + case LOGIN: + status("LOGIN"); + login(); + break; + case CHAR_SERVER: + status("CHAR_SERVER"); + char_server(); + break; + case CHAR_SELECT: + status("CHAR_SELECT"); + char_select(); + break; + case GAME: + sound.StopBGM(); + status("GAME"); + map_start(); + if( state==GAME ) + game(); + break; + default: + state = EXIT; + break; + } + } + status("EXIT"); + exit_engine(); + return 0; +} +END_OF_MAIN(); diff --git a/src/main.h b/src/main.h new file mode 100644 index 00000000..0b4eb87a --- /dev/null +++ b/src/main.h @@ -0,0 +1,86 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _MAIN_H +#define _MAIN_H + +#include <stdio.h> +#include <allegro.h> +#include <alfont.h> + +#include "log.h" +#include "game.h" +#include "./Net/protocol.h" +#include "./Gui/login.h" +#include "./Gui/gui.h" +#include "./Gui/char_server.h" +#include "./Gui/char_select.h" +#include "./Graphic/SuperEagle.h" +#include "./data/graphic/gfx_data.h" + +#define EXIT 0 +#define LOGIN 1 +#define CHAR_SERVER 2 +#define CHAR_SELECT 3 +#define CHAR_NEW 4 +#define CHAR_DEL 5 +#define GAME 6 + +typedef struct { + int address; + short port; + char name[20]; + short online_users; +} SERVER_INFO; + +typedef struct { + int id; + char name[24]; + short hp, max_hp, sp, max_sp, lv; + int xp, zeny, job_xp, job_lv; + short statp, skillp; + char STR, AGI, VIT, INT, DEX, LUK; +} CHAR_SEL_INFO; + +extern BITMAP *playerset; +extern DATAFILE *graphic, *emotions; +extern char username[25]; +extern char password[25]; +extern int map_address, char_ID; +extern short map_port; +extern char map_name[16]; +extern int account_ID, session_ID1, session_ID2; +extern char sex, n_server, n_character; +extern SERVER_INFO *server_info; +extern CHAR_SEL_INFO *char_info; +extern unsigned char state; +extern unsigned short x, y; +extern unsigned char direction; +extern unsigned short job, hair, hair_color; +extern unsigned char stretch_mode; + +#endif diff --git a/src/map.cpp b/src/map.cpp new file mode 100644 index 00000000..c8fc3612 --- /dev/null +++ b/src/map.cpp @@ -0,0 +1,120 @@ +/** + + 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 "map.h" +#include "log.h" +#include "being.h" + +#include <stdio.h> +#ifdef WIN32 +#include <windows.h> +#else +#include "./Net/win2linux.h" +#endif + +MAP map; + +/** Loads a map file */ +bool load_map(char *map_file) { + PACKFILE *file = pack_fopen(map_file, "rp"); + if(!file) { + warning(map_file); + return false; + } + pack_fread(&map, sizeof(MAP), file); + pack_fclose(file); + return true; +} + + +/** Set walkability flag for a tile */ +void set_walk(short x_c, short y_c, bool walkable) { + if(walkable==true)map.tiles[x_c][y_c].data[3] |= 0x0002; + else map.tiles[x_c][y_c].data[3] &= 0x00fd; +} + +/** Tell if a tile is walkable or not */ +bool get_walk(short x_c, short y_c) { + bool ret = (map.tiles[x_c][y_c].data[3] & 0x0002)>0; + if(ret==true) { + NODE *node = get_head(); + while(node && ret==true) { + if(get_x(node->coordinates)==x_c && get_y(node->coordinates)==y_c) + ret = false; + node = node->next; + } + return ret; + } else return false; +} + +/** Tell if a tile is walkable or not (0=walkable,1=not walkable) */ +unsigned char get_path_walk(unsigned short x, unsigned short y) { + if(get_walk(x, y))return 0; + else return 1; +} + +/** Tell if a tile is animated or not */ +bool get_anim(short x_c, short y_c, char layer) { + char temp = map.tiles[x_c][y_c].flags & 0x00C0; + temp>>=6; + if(abs(temp)==layer)return (map.tiles[x_c][y_c].data[3] & 0x0001)>0; + else return false; +} + +/** Set tile ID */ +void set_tile(short x_c, short y_c, char layer, unsigned short id) { + if(layer==0) { + id <<= 6; + map.tiles[x_c][y_c].data[0] = HIBYTE(id); + map.tiles[x_c][y_c].data[1] &= 0x003f; + map.tiles[x_c][y_c].data[1] |= LOBYTE(id); + } else if(layer==1) { + id <<= 4; + map.tiles[x_c][y_c].data[1] &= 0x00c0; + map.tiles[x_c][y_c].data[1] |= HIBYTE(id); + map.tiles[x_c][y_c].data[2] &= 0x000f; + map.tiles[x_c][y_c].data[2] |= LOBYTE(id); + } else if(layer==2) { + id <<= 2; + map.tiles[x_c][y_c].data[2] &= 0x00f0; + map.tiles[x_c][y_c].data[2] |= HIBYTE(id); + map.tiles[x_c][y_c].data[3] &= 0x0003; + map.tiles[x_c][y_c].data[3] |= LOBYTE(id); + } +} + +/** Return tile ID */ +unsigned short get_tile(short x_c, short y_c, char layer) { + unsigned short id; + if(layer==0) { + id = MAKEWORD(map.tiles[x_c][y_c].data[1] & 0x00c0, map.tiles[x_c][y_c].data[0]); + id >>= 6; + } else if(layer==1) { + id = MAKEWORD(map.tiles[x_c][y_c].data[2] & 0x00f0, map.tiles[x_c][y_c].data[1] & 0x003f); + id >>= 4; + } else if(layer==2) { + id = MAKEWORD(map.tiles[x_c][y_c].data[3] & 0x00fc, map.tiles[x_c][y_c].data[2] & 0x000f); + id >>=2; + } + return id; +} diff --git a/src/map.h b/src/map.h new file mode 100644 index 00000000..4c33928d --- /dev/null +++ b/src/map.h @@ -0,0 +1,89 @@ +/** + + 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 + +*/ + +#ifndef _MAP_H +#define _MAP_H + +#define MAP_WIDTH 200 +#define MAP_HEIGHT 200 +#define TILESET_WIDTH 30 + +#define WALKABLE 0 +#define NOT_WALKABLE 1 + +/** Struct representing a tile. A tile is composed of 3 layers. +1st: Ground layer (used for grass, water, ...) -> bit 0-9 of data +2nd: Fringe layer (objects that are overlapped by the player) -> bit 10-19 of data +3rd: Over layer (roofs, tree leaves, ...) -> bit 20-29 +Walk flag: tells if a tile is walkable or not -> bit 30 +Animation flag: tells if a tile is animated or not -> bit 31 + +data field: +1st byte: [1][1][1][1][1][1][1][1] +2nd byte: [1][1][2][2][2][2][2][2] +3rd byte: [2][2][2][2][3][3][3][3] +4th byte: [3][3][3][3][3][3][W][A] + +Legend: +1 - First layer +2 - Second layer +3 - Third layer +W - Walkability flag +A - Animated tile flag + +flags field: +[l][l][f][f][f][f][f][f][f] + +Legend: +l - Animated layer +f - future use +*/ +struct TILE { + char data[4]; + char flags; +}; + +struct MAP { + TILE tiles[MAP_WIDTH][MAP_HEIGHT]; + char tileset[20]; + char bg_music[20]; +}; + +bool load_map(char *map_file); +void set_walk(short x_c, short y_c, bool walkable); +bool get_walk(short x_c, short y_c); +unsigned char get_path_walk(unsigned short x, unsigned short y); +bool get_anim(short x_c, short y_c, char layer); +void set_tile(short x_c, short y_c, char layer, unsigned short id); +unsigned short get_tile(short x_c, short y_c, char layer); + +/** Return tile x coordinate in tileset */ +inline int get_tile_x(int x, int y, int layer) { + return get_tile(x,y,layer)%TILESET_WIDTH; +} +/** Return tile y coordinate in tileset */ +inline int get_tile_y(int x, int y, int layer) { + return get_tile(x,y,layer)/TILESET_WIDTH; +} + +#endif diff --git a/src/net/network.cpp b/src/net/network.cpp new file mode 100644 index 00000000..c8bff09a --- /dev/null +++ b/src/net/network.cpp @@ -0,0 +1,177 @@ +/** + + 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 "network.h" +#ifndef WIN32 +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#endif + +/** Warning: buffers and other variables are shared, + so there can be only one connection active at a time */ + +int buffer_size = 65536; +char *in, *out; +int in_size, out_size; + +SOCKET sock; +SOCKADDR_IN addr; +// File descriptors attached to socket +fd_set read_socket; +fd_set write_socket; + +/** Increase size of written data */ +void WFIFOSET(int len) { + if(out_size+len>=buffer_size) + warning("Output buffer full"); + else out_size+=len; +} + +/** Convert an address from int format to string */ +char *iptostring(int address) { + short temp1, temp2; + + char *temp = (char *)malloc(sizeof(char[20])); + temp1 = LOWORD(address); + temp2 = HIWORD(address); + sprintf(temp, "%i.%i.%i.%i", LOBYTE(temp1), HIBYTE(temp1), LOBYTE(temp2), HIBYTE(temp2)); + return temp; +} + +/** Open a session with a server */ +SOCKET open_session(const char* address, short port) { + #ifdef WIN32 + WSADATA wsda; + #endif + struct hostent *server; + int ret; + + // Init WinSock and connect the socket + #ifdef WIN32 + WSAStartup(MAKEWORD(2,0), &wsda); + #endif + + sock = socket(PF_INET, SOCK_STREAM, 0); // Create socket for current session + if(sock==SOCKET_ERROR)return SOCKET_ERROR; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(address); + if(addr.sin_addr.s_addr == INADDR_NONE){ + server = NULL; + server = gethostbyname(address); + if(server == NULL)return SOCKET_ERROR; + memcpy(&addr.sin_addr, server->h_addr_list[0], server->h_length); + } + + ret = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); + if(ret == SOCKET_ERROR)return SOCKET_ERROR; + + // Init buffers + in = (char *)malloc(buffer_size); + out = (char *)malloc(buffer_size); + memset(in, '\0', buffer_size); + memset(out, '\0', buffer_size); + in_size = 0; + out_size = 0; + FD_CLR(sock, &read_socket); + FD_CLR(sock, &write_socket); + + return sock; +} + +/** Close a session */ +void close_session() { + FD_CLR(sock,&read_socket); + FD_CLR(sock,&write_socket); + closesocket(sock); + if(in!=NULL)free(in); + if(out!=NULL)free(out); + in = NULL; + out = NULL; + in_size = 0; + out_size = 0; + WSACleanup(); +} + +/** Send and receive data waiting in the buffers */ +void flush() { + int ret = 0; + void *buf = out; //-kth5 + timeval time_out; + + // Init the time_out struct to 0s so it won't block + time_out.tv_sec=0; + time_out.tv_usec=0; + + // Clear file descriptors and set them to socket + FD_ZERO(&read_socket); + FD_ZERO(&write_socket); + FD_SET(sock, &read_socket); + FD_SET(sock, &write_socket); + + // Check if socket has available data by evaluating attached file descriptors + select(FD_SETSIZE, &read_socket, &write_socket, NULL, &time_out); + + // Send data if available + if(FD_ISSET(sock, &write_socket)) { + // While there wasn't a error or sent the whole data: handles partial packet send + while((ret!=SOCKET_ERROR)&&(out_size>0)) { + ret = send(sock, (char *)buf, out_size, 0); + /*FILE *file = fopen("log.log","wb"); + + fprintf(file, "%s", out[0]); + fclose(file);*/ + // If not the whole data has been sent, empty the buffer from already sent bytes + if(ret!=SOCKET_ERROR && ret>0) { + buf = (char*)buf+ret; //-kth5 + out_size -= ret; + } + } + if(ret==SOCKET_ERROR) { + error("Socket Error"); +#ifdef WIN32 + log_int("Error", "socket_error", WSAGetLastError()); +#else + log("Error", "socket_error", "Undefined socket error"); +#endif + } + } + + // Read data, if available + if(FD_ISSET(sock, &read_socket)) { + /* There's no check for partial received packets because at this level + the app doesn't know packet length, but it will done when parsing received data */ + ret = recv(sock, in+in_size, RFIFOSPACE, 0); + if(ret==SOCKET_ERROR) { +#ifdef WIN32 + log_int("Error", "socket_error", WSAGetLastError()); +#else + log("Error", "socket_error", "Undefined socket error"); +#endif + } else RFIFOSET(ret); // Set size of available data to read + } +} + + diff --git a/src/net/network.h b/src/net/network.h new file mode 100644 index 00000000..5eca3398 --- /dev/null +++ b/src/net/network.h @@ -0,0 +1,87 @@ +/** + + 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 + +*/ + +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _NETWORK_H +#define _NETWORK_H + +#ifdef WIN32 +#include <allegro.h> +#include <winalleg.h> +#else +#include "win2linux.h" +#endif +#include <stdio.h> +#include "../log.h" +#ifdef MACOSX +#include "win2mac.h" +#endif + +/** Macros to write in output buffer, pos is the location where to write data + After you wrote len bytes, you have to use WFIFOSET */ +#define WFIFOSPACE (buffer_size-out_size) // Number of bytes currently written in uotput buffer +#define WFIFOP(pos) (out+(pos+out_size)) // Return a pointer to a specific location in output buffer +#define WFIFOB(pos) (*(unsigned char *)(out+pos+out_size)) // Write a byte (1 byte) +#define WFIFOW(pos) (*(unsigned short *)(out+pos+out_size)) // Write a word (2 byte) +#define WFIFOL(pos) (*(unsigned int *)(out+pos+out_size)) // Write a long (4 byte) +//#define WFIFOSET(len) out_size+=len // Increase size of written data +#ifdef MACOSX +#define net_b_value(id) (id) +#define net_w_value(id) DR_SwapTwoBytes(id) +#define net_l_value(id) DR_SwapFourBytes(id) +#else +#define net_b_value(id) (id) +#define net_w_value(id) (id) +#define net_l_value(id) (id) +#endif + + +/** Macros to read from input buffer, pos is the location of data to be read + After you read len bytes, you should use RFIFOSKIP */ +#define RFIFOP(pos) (in+(pos)) // Get a pointer from a specific location in input buffer +#ifdef MACOSX +#define RFIFOB(pos) ((*(unsigned char*)(in+(pos)))) // Read a byte +#define RFIFOW(pos) DR_SwapTwoBytes((*(unsigned short*)(in+(pos)))) // Read a word +#define RFIFOL(pos) DR_SwapFourBytes((*(unsigned int*)(in+(pos)))) // Read a long +#else +#define RFIFOB(pos) (*(unsigned char*)(in+(pos))) // Read a byte +#define RFIFOW(pos) (*(unsigned short*)(in+(pos))) // Read a word +#define RFIFOL(pos) (*(unsigned int*)(in+(pos))) // Read a long +#endif +#define RFIFOSKIP(len) (memcpy(in,in+len,in_size-len));in_size-=len; // Empty len bytes from input buffer +#define RFIFOSPACE (buffer_size-in_size) // Return input buffer size +#define RFIFOSET(len) in_size+=len; + +void WFIFOSET(int len); +char *iptostring(int address); +SOCKET open_session(const char* address, short port); +void close_session(); +void flush(); + +extern char *in, *out; // Input, output buffer +extern int in_size, out_size; // Input, output buffer size + +#endif diff --git a/src/net/protocol.cpp b/src/net/protocol.cpp new file mode 100644 index 00000000..2c33d092 --- /dev/null +++ b/src/net/protocol.cpp @@ -0,0 +1,264 @@ +/** + + 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 "protocol.h" + +short packet_lengths[] = { + + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// #0x0040 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, + 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, +// #0x0080 + 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, + 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, + 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, + 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, +// #0x00C0 + 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, + 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, + 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, + 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, +// #0x0100 + 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, + 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, + 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, + 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, +// #0x0140 + 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, + 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, + -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, + 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, +// #0x0180 + 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, + 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6, + 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, + 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, +// #0x01C0 + 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28, + 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, + 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, + -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, +// #0x200 + 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, + +}; + +/** Packet length by id */ +short get_length(short id) { + return packet_lengths[id]; +} + +/** Decodes dest x coord */ +unsigned short get_dest_x(char *data) { + short temp; + temp = MAKEWORD(data[3], data[2] & 0x000f); + temp >>= 2; + return temp; +} + +/** Decodes dest y coord */ +unsigned short get_dest_y(char *data) { + short temp; + temp = MAKEWORD(data[4], data[3] & 0x0003); + return temp; +} + +/** Decodes src x coord */ +unsigned short get_src_x(char *data) { + short temp; + temp = MAKEWORD(data[1], data[0]); + temp >>= 6; + return temp; +} + +/** Decodes src y coord */ +unsigned short get_src_y(char *data) { + short temp; + temp = MAKEWORD(data[2], data[1] & 0x003f); + temp >>= 4; + return temp; +} + +/** Decodes src direction */ +unsigned char get_src_direction(char data) { + data >>= 4; + return data; +} + +/** Decodes dest direction */ +unsigned char get_dest_direction(char data) { + return data & 0x000f; +} + +/** Decodes x coord */ +unsigned short get_x(char *data) { + short temp; + temp = MAKEWORD(data[1] & 0x00c0, data[0] & 0x00ff); + temp >>= 6; + return temp; +} + +/** Decodes y coord */ +unsigned short get_y(char *data) { + short temp; + if(!data)error("Corrupted data"); + temp = MAKEWORD(data[2] & 0x00f0, data[1] & 0x003f); + if(!temp)error("Corrupted data"); + temp >>= 4; + return temp; +} + +/** Decodes direction */ +unsigned char get_direction(char *data) { + char temp; + temp = data[2] & 0x000f; + return temp; +} + +/** Encodes coords and direction in 3 bytes data */ +void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction) { + short temp; + temp = x; + temp <<= 6; + data[0] = 0; + data[1] = 1; + data[2] = 2; + data[0] = HIBYTE(temp); + data[1] = LOBYTE(temp); + temp = y; + temp <<= 4; + data[1] |= HIBYTE(temp); + data[2] = LOBYTE(temp); + data[2] |= direction; +} + +/** Initialize connection with map server */ +void map_start() { + // Connect to map server + if(open_session(iptostring(map_address), map_port)==SOCKET_ERROR) { + warning("Unable to connect to map server"); + state = LOGIN; + ok("Error", "Unable to connect to map server"); + return; + } + + // Send login infos + WFIFOW(0) = net_w_value(0x0072); + WFIFOL(2) = net_l_value(account_ID); + WFIFOL(6) = net_l_value(char_ID); + WFIFOL(10) = net_l_value(session_ID1); + WFIFOL(14) = net_l_value(session_ID2); + WFIFOB(18) = net_b_value(sex); + WFIFOSET(19); + + while((in_size<4)||(out_size>0))flush(); + RFIFOSKIP(4); + + while(in_size<2)flush(); + + if(RFIFOW(0)==0x0073) { + while(in_size<11)flush(); + x = get_x(RFIFOP(6)); + y = get_y(RFIFOP(6)); + log_int("Player", "x", x); + log_int("Player", "y", y); + direction = get_direction(RFIFOP(6)); + log_int("Player", "direction", direction); + RFIFOSKIP(11); + } else if(0x0081) { + warning("Map server D/C"); + } else error("Unknown packet: map_start"); + // Send "map loaded" + WFIFOW(0) = net_w_value(0x007d); + WFIFOSET(2); + while(out_size>0)flush(); +} + +/** Requests to walk */ +void walk(unsigned short x, unsigned short y, unsigned char direction) { + char temp[3]; + set_coordinates(temp, x, y, direction); + WFIFOW(0) = net_w_value(0x0085); + memcpy(WFIFOP(2), temp, 3); + WFIFOSET(5); +} + +/** Request to speak */ +void speak(char *speech) { + int len = (int)strlen(speech); + WFIFOW(0) = net_w_value(0x008c); + WFIFOW(2) = net_w_value(len+4); + memcpy(WFIFOP(4), speech, len); + WFIFOSET(len+4); +} + +/** request action */ +void action(short type, int id) { + WFIFOW(0) = net_w_value(0x0089); + WFIFOL(2) = net_l_value(id); + WFIFOB(6) = net_l_value(type); + WFIFOSET(7); +} + + +/** Request to attack */ +void attack(unsigned short x, unsigned short y, unsigned char direction) { + int monster_id = 0; + + if(direction==2) { + for(int j=-1;j<2;j++) + for(int i=-1;i<1;i++) { + monster_id = find_monster(x+i, y+j); + if(monster_id!=0) + action(0x07, monster_id); + } + } else if(direction==6) { + for(int j=-1;j<2;j++) + for(int i=0;i<2;i++) { + monster_id = find_monster(x+i, y+j); + if(monster_id!=0) + action(0x07, monster_id); + } + } else if(direction==4) { + for(int j=-1;j<1;j++) + for(int i=-1;i<2;i++) { + monster_id = find_monster(x+i, y+j); + if(monster_id!=0) + action(0x07, monster_id); + } + } else if(direction==0) { + for(int j=0;j<1;j++) + for(int i=-1;i<2;i++) { + monster_id = find_monster(x+i, y+j); + if(monster_id!=0) + action(0x07, monster_id); + } + } +} + diff --git a/src/net/protocol.h b/src/net/protocol.h new file mode 100644 index 00000000..60293e4a --- /dev/null +++ b/src/net/protocol.h @@ -0,0 +1,57 @@ +/** + + 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 + +*/ +#ifdef WIN32 + #pragma warning (disable:4312) +#endif + +#ifndef _PROTOCOL_H +#define _PROTOCOL_H + +#include <allegro.h> +#ifdef WIN32 +#include <winalleg.h> +#include <windows.h> +#else +#include "win2linux.h" +#endif + +#include "../main.h" +#include "../being.h" + +short get_length(short id); +unsigned short get_x(char *data); +unsigned short get_y(char *data); +unsigned char get_direction(char *data); +unsigned short get_src_x(char *data); +unsigned short get_src_y(char *data); +unsigned char get_src_direction(char data); +unsigned short get_dest_x(char *data); +unsigned short get_dest_y(char *data); +unsigned char get_dest_direction(char data); +void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); +void map_start(); +void walk(unsigned short x, unsigned short y, unsigned char direction); +void speak(char *speech); +void attack(unsigned short x, unsigned short y, unsigned char direction); +void action(short, int); +#endif diff --git a/src/net/win2linux.h b/src/net/win2linux.h new file mode 100644 index 00000000..71737a58 --- /dev/null +++ b/src/net/win2linux.h @@ -0,0 +1,54 @@ +#include <stdio.h> +#include <stdlib.h> +#ifndef MACOSX +#include <malloc.h> +#endif +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <netdb.h> + +typedef unsigned char BYTE; +typedef unsigned short WORD; +#define MAKEWORD(low,high) \ + ((WORD)(((BYTE)(low)) | ((WORD)((BYTE)(high))) << 8)) +#define closesocket(a) close(a) +#define SOCKET int +#define SOCKET_ERROR -1 +#define SOCKADDR_IN struct sockaddr_in +typedef struct sockaddr SOCKADDR; +typedef SOCKADDR * LPSOCKADDR; +#define WSACleanup() ; + + + +typedef unsigned short WORD; +typedef unsigned long int LWORD; +typedef unsigned char BYTE; +#define LOBYTE(w) ((BYTE) (w) ) +#define HIBYTE(w) ((BYTE) (((WORD)(w)) >> 8) ) +#define LOWORD(l) ((WORD) (l) ) +#define HIWORD(l) ((WORD) (((LWORD)(l)) >> 16) ) +#define HANDLE int +#define HANDLE int +#define PHANDLE int +#define SMALL_RECT int +//#define WORD int +#define DWORD int +#define PDWORD int +#define BOOL int +#define LPBOOL int +#define LPSTR int +#define LPTSTR int +#define LPCTSTR int +#define LPDWORD int +#define LPVOID int +#define WINAPI + +#define LOBYTE(w) ((BYTE) (w) ) +#define HIBYTE(w) ((BYTE) (((WORD)(w)) >> 8) ) +#define LPTHREAD_START_ROUTINE void *(*)(void *) +#define CloseHandle close diff --git a/src/net/win2mac.cpp b/src/net/win2mac.cpp new file mode 100644 index 00000000..be4bad1d --- /dev/null +++ b/src/net/win2mac.cpp @@ -0,0 +1,42 @@ +#include "win2mac.h" + + +void dummy() +{ + +printf("hej\n"); +} + +UInt32 DR_SwapFourBytes(UInt32 dw) +{ +UInt32 tmp; +tmp = (dw & 0x000000FF); +tmp = ((dw & 0x0000FF00) >> 0x08) | (tmp << 0x08); +tmp = ((dw & 0x00FF0000) >> 0x10) | (tmp << 0x08); +tmp = ((dw & 0xFF000000) >> 0x18) | (tmp << 0x08); +return (tmp); +} + +UInt16 DR_SwapTwoBytes(UInt16 w) +{ +UInt16 tmp; +tmp = (w & 0x00FF); +tmp = ((w & 0xFF00) >> 0x08) | (tmp << 0x08); +return(tmp); +} + +char* SwapChar(char charlist[]) +{ +for(int i =0; i< 24/2; i++) + SWAP(charlist[i],charlist[24-i]); +return charlist; +} + +/* +char* SwapChar(char charlist[]) +{ +for(int i =0; i< sizeof(charlist)*4/2; i++) + SWAP(charlist[i],charlist[sizeof(charlist)*4-i]); +return charlist; +} +*/
\ No newline at end of file diff --git a/src/net/win2mac.h b/src/net/win2mac.h new file mode 100644 index 00000000..72ae9fd9 --- /dev/null +++ b/src/net/win2mac.h @@ -0,0 +1,16 @@ +#include <stdio.h> + +#define UInt16 unsigned short int +#define INT16 short int +#define UInt32 unsigned long int +#define INT32 long int +#define SWAP( a, b ) { char c; c=a; a=b; b=c; } + +#ifndef _MAC_H +#define _MAC_H +void dummy(); +UInt32 DR_SwapFourBytes(UInt32 dw); +UInt16 DR_SwapTwoBytes(UInt16 w); +char* SwapChar(char charlist[]); +#endif + diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp new file mode 100644 index 00000000..105959ba --- /dev/null +++ b/src/sound/sound.cpp @@ -0,0 +1,269 @@ +/** + + 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 + +*/ + +/** + rewrite of non-existend sdl-soundengine using allegro + + Author: kth5 aka Alexander Baldeck + pipe your question, suggestions and flames to: kth5@gawab.com +*/ + +#ifdef WIN32 + #pragma warning(disable:4312) +#endif + +#include <allegro.h> + + + #include <jgmod.h> + #include "sound.h" + +/** + install the sound engine + int voices -> overall reserved voices + int mod_voices -> voices dedicated for mod-playback + + NOTE: + overall voices must not be less or equal to the + specified amount of mod_voices! + if mod-voices is too low some mods will not sound + correctly since a couple of tracks are not going + to be played along w/ the others. so missing ins- + truments can be a result. + 32/20 sounds realistic here. +*/ +void TmwSound::Init(int voices, int mod_voices) { + isOk = -1; + + if(mod_voices >= voices) + throw("No voices left for SFX! Sound will be disabled!"); + + install_timer(); + reserve_voices (voices, -1); + + #ifdef WIN32 + if (install_sound (DIGI_AUTODETECT, MIDI_AUTODETECT, NULL) < 0) + #else + if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0) + #endif + throw("Could not initialize sound... :-("); + + + if (install_mod (mod_voices) < 0) + throw("Could not install MOD player... :-("); + + mod = NULL; + mid = NULL; + sfx = NULL; + + pan = 128; + pitch=1000; + + isOk = 0; +} + +/** + set the volume value-range: 0-255 + int digi -> for digital playback + int mid -> for midi playback + int mod -> for... aw, you guess ^^ + + NOTE: + all values may only be between 0-255 where 0 means + muted. +*/ +void TmwSound::SetVol(int digi, int mid, int mod) { + if(isOk==-1) + return; + set_volume(digi, mid); + set_mod_volume(mod); + set_hardware_volume(digi, mid); + + if(isMaxVol(vol_digi + digi)==false) vol_digi += digi; + if(isMaxVol(vol_midi + mid) ==false) vol_midi += mid; + if(isMaxVol(vol_mod + mod) ==false) vol_mod += mod; +} + +/** + adjusts current volume + int digi -> for digital playback + int mid -> for midi playback + int mod -> for... aw, you guess ^^ + + NOTE: + all values may only be between 0-255 where 0 means + muted. +*/ +void TmwSound::SetAdjVol(int adigi, int amid, int amod) { + if(isOk==-1) + return; + set_volume(vol_digi + adigi, vol_midi + amid); + set_mod_volume(vol_mod + amod); + + if(isMaxVol(vol_digi + adigi)==false) vol_digi += adigi; + if(isMaxVol(vol_midi + amid) ==false) vol_midi += amid; + if(isMaxVol(vol_mod + amod) ==false) vol_mod += amod; +} + +/** + start BGM using a midi file + char *in -> full path of midi file + int loop -> how many times should the midi be looped? (-1 = infinite) + + NOTE: + playing midi does not steal away any voices but + does not work w/ most soundcards w/o software + emulation. this means than linux-users will most + probably be left out. do not use this unless we + find a way to always get it to work. :-) + + at this point of time only standard RMI midi files + can be played. so no m$ extensions like GS and stuff. +*/ +void TmwSound::StartMIDI(char *in, int loop) { + if(isOk==-1) + return; + + mid = load_midi(in); + if (!mid) { + isOk=-1; + throw("Could not load MIDI file!"); + } + + play_midi(mid, TRUE); +} + +/** + start BGM using a mod file + char *in -> full path of mod file + int loop -> how many times should the midi be looped? (-1 = infinite) + + NOTE: + playing mod is a pretty good choice. most of the work + is being done by the cpu so it's not dependend on the + sound-card how things sound as long as it works. ;-) + + JGMOD supports several formats: + MOD + S3M + XM + Unreal + and S3M (in UMX extension) +*/ +void TmwSound::StartMOD(char * in, int loop) { + if(isOk==-1) + return; + + mod = load_mod(in); + if(!mod) { + isOk=-1; + throw("Error reading MOD file..."); + } + play_mod(mod, TRUE); +} + +/** + stop all currently running BGM tracks + + NOTE: + you need to stop all playback when you want to + switch from mod to midi. playing a new track is + usually possibe simply by calling StartMIDI() ir + SartMOD() again. + passing NULL to the playing functions only means + to make playback stop. +*/ +void TmwSound::StopBGM() { + if(isOk==-1) + return; + + play_midi(NULL,-1); + stop_mod(); + + mod = NULL; + mid = NULL; +} + +/** + play short sample usually for sfx + char * in -> full path to the sample file + (int wavid -> the id of the preloaded wav file (not implemented yet)) + int pan -> panning of the sound, values can be 0-255 where 128 is + the middle + + NOTE: + later on this will be a subsequent call to another + function that preloads all wavs corresponding to + the current area (e.g. monster screams) to memory. + right now the function loads the file from hdd + everytime you want it to be played. this is kind of + resource intensive even though most OS'ses cache a + already loaded file for some time. + + allegro supports different formats but this is not + stated clear enough - these will work for sure: + WAV + VOC + + i don't know what kind of samples are necessary so we + need to test this thoroughly. +*/ +void TmwSound::StartWAV(char * in, int pan) { + if(isOk==-1) + return; + + sfx = load_sample(in); + if (!sfx) + throw("Error reading WAV file..."); + + play_sample(sfx, vol_digi, pan, pitch, FALSE); +} + +/** + deinstall all sound functionality + + NOTE: + normally you won't need to call this since this is + done by allegro when shutting itself down. but if + you find a reason to delete the sound-engine from + memory (e.g. garbage-collection) feel free to use + it. :-P +*/ +int TmwSound::Close(void) { + if(isOk==-1) + return -1; + mod = NULL; + mid = NULL; + sfx = NULL; + + remove_sound(); + isOk = -1; + return 0; +} + +/** PRIVATE */ + +bool TmwSound::isMaxVol(int vol) { + if( vol > 0 && vol < 255 ) return false; + else return true; +} diff --git a/src/sound/sound.h b/src/sound/sound.h new file mode 100644 index 00000000..12300e58 --- /dev/null +++ b/src/sound/sound.h @@ -0,0 +1,83 @@ +/** + + 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 + +*/ + +#ifndef __SOUND_H +#define __SOUND_H + +#ifdef WIN32 + #pragma warning(disable:4312) +#endif +#include <allegro.h> +#include <jgmod.h> +#include <string> +using namespace std; + +/** + rewrite of non-existend sdl-soundengine using allegro + + Author: kth5 aka Alexander Baldeck + pipe your question, suggestions and flames to: kth5@gawab.com + + NOTE: + i documented all functions in their implementation. ;-) +*/ + +class TmwSound { + public: + void Init(int, int); + int Close(); + + void StartMIDI(char *, int); + void StartMOD(char *, int); + void StopBGM(); + + void StartWAV(char *, int); + void SetVol(int, int, int); + void SetAdjVol(int adigi, int amid, int amod); + + TmwSound() {isOk=-1;} + ~TmwSound() {StopBGM(); Close();}; // if allegros shuts down or object is deleted + // any BGM is stopped and SFX runout + private: + int isOk; // initial value is -1 which means error. + // you can only play sounds and bgm if this is 0. + // should be the case after calling Init() + // successfully + + MIDI * mid; + JGMOD * mod; + SAMPLE * sfx; + + int pan; + int pitch; + + int ret; + int vol_digi; + int vol_midi; + int vol_mod; + + bool isMaxVol(int); +}; + + +#endif |