Procházet zdrojové kódy

fixing and optimizing sharp shooting, should work now

git-svn-id: https://svn.code.sf.net/p/rathena/svn/branches/stable@1091 54d463be-8e91-2dee-dedb-b68131a5f0ec
(no author) před 20 roky
rodič
revize
d1518bb819
2 změnil soubory, kde provedl 338 přidání a 5 odebrání
  1. 3 0
      Changelog-SVN.txt
  2. 335 5
      src/map/map.c

+ 3 - 0
Changelog-SVN.txt

@@ -1,6 +1,9 @@
 Date	Added
 
 02/12
+        * fixing and optimizing sharp shooting
+          have implemented two different versions, just have a look and 
+          decide which to use, description is in the code [Shinomori]
         * Changed default map cache path from db/map.info to save/mapinfo.txt [celest]
         * Removed old code for Sharp Shooting (still a little buggy) [celest]
         * Merged Shinomori's code into map_foreachinpath [celest]

+ 335 - 5
src/map/map.c

@@ -46,6 +46,10 @@
 #include "memwatch.h"
 #endif
 
+
+// maybe put basic macros to somewhere else
+#define swap(a,b) ((a == b) || ((a ^= b), (b ^= a), (a ^= b)))
+
 unsigned long ticks = 0; // by MC Cameri
 
 #ifndef TXT_ONLY
@@ -152,10 +156,10 @@ struct charid2nick {
 	int req_id;
 };
 
-// ォ゙ォテォラォュォ罩テォキォ袮ラ鯑ォユォ鬮ー(map_athana.conf?ェホread_map_from_cacheェヌ�)
+// ォ゙ォテォラォュォ罩テォキォ袮ラ鯑ォユォ鬮ー(map_athana.conf?ェホread_map_from_cacheェヌ�)
 // 0:ララ鯑ェキェハェ、 1:゙ェ?�ワチ� 2:?�ワチ�
 int  map_read_flag = READ_FROM_GAT;
-char map_cache_file[256]="db/map.info"; // ォ゙ォテォラォュォ罩テォキォ雖ユォ。ォ、ォ
+char map_cache_file[256]="db/map.info"; // ォ゙ォテォラォュォ罩テォキォ雖ユォ。ォ、ォ
 
 char motd_txt[256] = "conf/motd.txt";
 char help_txt[256] = "conf/help.txt";
@@ -397,7 +401,7 @@ int map_count_oncell(int m, int x, int y) {
 	return count;
 }
 /*
- * ォサォ�セェホ��ェヒフクェトェアェソォケォュォ�讚ヒォテォネェ��ケ
+ * ォサォ・セェホ��ェヒフクェトェアェソォケォュォ・讚ヒォテォネェ��ケ
  */
 struct skill_unit *map_find_skill_unit_oncell(int m,int x,int y,int skill_id)
 {
@@ -657,8 +661,9 @@ void map_foreachincell(int (*func)(struct block_list*,va_list),int m,int x,int y
 * For checking a path between two points (x0, y0) and (x1, y1)
 *------------------------------------------------------------
  */
-void map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int type,...) {
-	va_list ap;
+void map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int length,int type,...) 
+{
+/*	va_list ap;
 	double deltax = 0.0;
 	double deltay = 0.0;
 	int t, bx, by;
@@ -707,6 +712,15 @@ void map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int
 	}
 
 	if (type == 0 || type != BL_MOB)
+
+
+this here is  wrong, 
+there is no check if x0<x1 and y0<y1 
+but this is not valid in 3 of 4 cases, 
+so in this case here you check only blocks when shooting to a positive direction
+shooting in other directions just do nothing like the skill has failed
+if you want to keep this that way then check and swap x0,y0 with x1,y1
+
 		for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) {
 			for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){
 				bl = map[m].block[bx+by*map[m].bxs];
@@ -754,6 +768,322 @@ void map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int
 	aFree (xs);
 	aFree (ys);
  	va_end(ap);	
+
+*/
+
+/*
+//////////////////////////////////////////////////////////////
+//
+// sharp shooting 1
+//
+//////////////////////////////////////////////////////////////
+// problem: 
+// finding targets standing on and within some range of a line
+// (t1,t2 t3 and t4 get hit)
+//
+//     target 1
+//      x t4
+//     t2
+// t3 x
+//   x
+//  S
+//////////////////////////////////////////////////////////////
+// solution 1 (straight forward, but a bit calculation expensive)
+// calculating perpendiculars from quesionable mobs to the straight line
+// if the mob is hit then depends on the distance to the line
+// 
+// solution 2 (complex, need to handle many cases, but maybe faster)
+// make a formula to deside if a given (x,y) is within a shooting area
+// the shape can be ie. rectangular or triangular
+// if the mob is hit then depends on if the mob is inside or outside the area
+// I'm not going to implement this, but if somebody is interested
+// in vector algebra, it might be some fun 
+
+//////////////////////////////////////////////////////////////
+// possible shooting ranges (I prefer the second one)
+//////////////////////////////////////////////////////////////
+//
+//  ----------------                     ------
+//  ----------------               ------------
+// Sxxxxxxxxxxxxxxxxtarget    Sxxxxxxxxxxxxxxxxtarget
+//  ----------------               ------------
+//  ----------------                      -----
+//
+// the original code implemented the left structure
+// might be not that realistic, so I changed to the other one
+// I take "range" as max distance from the line
+//////////////////////////////////////////////////////////////
+
+	va_list ap;
+	int i, blockcount = bl_list_count;
+	struct block_list *bl;
+	int c1,c2;
+
+///////////
+	double deltax,deltay;
+	double k,kfact,knorm;
+	double v1,v2,distance;
+	double xm,ym,rd;
+	int bx,by,bx0,bx1,by0,by1;
+//////////////
+	// no map
+	if(m < 0) return;
+
+	// xy out of range
+	if (x0 < 0) x0 = 0;
+	if (y0 < 0) y0 = 0;
+	if (x1 >= map[m].xs) x1 = map[m].xs-1;
+	if (y1 >= map[m].ys) y1 = map[m].ys-1;
+
+	///////////////////////////////
+	// stuff for a linear equation in xy coord to calculate 
+	// the perpendicular from a block xy to the straight line
+	deltax = (x1-x0);
+	deltay = (y1-y0);
+	kfact = (deltax*deltax+deltay*deltay);	// the sqare length of the line
+	knorm = -deltax*x0-deltay*y0;			// the offset vector param
+
+//printf("(%i,%i)(%i,%i) range: %i\n",x0,y0,x1,y1,range);
+
+	if(kfact==0) return; // shooting at the standing position should not happen
+	kfact = 1/kfact; // divide here and multiply in the loop
+
+	range *= range; // compare with range^2 so we can skip a sqrt and signs
+
+	///////////////////////////////
+	// prepare shooting area check
+	xm = (x1+x0)/2.0;
+	ym = (y1+y0)/2.0;// middle point on the shooting line
+	// the sqared radius of a circle around the shooting range
+	// plus the sqared radius of a block
+	rd = (x0-xm)*(x0-xm) + (y0-ym)*(y0-ym) + (range*range)
+					+BLOCK_SIZE*BLOCK_SIZE/2;
+	// so whenever a block midpoint is within this circle
+	// some of the block area is possibly within the shooting range
+
+	///////////////////////////////
+	// what blocks we need to test
+	// blocks covered by the xy position of begin and end of the line
+	bx0 = x0/BLOCK_SIZE;
+	bx1 = x1/BLOCK_SIZE;
+	by0 = y0/BLOCK_SIZE;
+	by1 = y1/BLOCK_SIZE;
+	// swap'em for a smallest-to-biggest run
+	if(bx0>bx1)	swap(bx0,bx1);
+	if(by0>by1)	swap(by0,by1);
+
+	// enlarge the block area by a range value and 1
+	// so we can be sure to process all blocks that might touch the shooting area
+	// in this case here with BLOCK_SIZE=8 and range=2 it will be only enlarged by 1
+	// but I implement it anyway just in case that ranges will be larger 
+	// or BLOCK_SIZE smaller in future
+	i = (range/BLOCK_SIZE+1);//temp value
+	if(bx0>i)				bx0 -=i; else bx0=0;
+	if(by0>i)				by0 -=i; else by0=0;
+	if(bx1+i<map[m].bxs)	bx1 +=i; else bx1=map[m].bxs-1;
+	if(by1+i<map[m].bys)	by1 +=i; else by1=map[m].bys-1;
+
+
+//printf("run for (%i,%i)(%i,%i)\n",bx0,by0,bx1,by1);
+	for(bx=bx0; bx<=bx1; bx++)
+	for(by=by0; by<=by1; by++)
+	{	// block xy
+		c1  = map[m].block_count[bx+by*map[m].bxs];		// number of elements in the block
+		c2  = map[m].block_mob_count[bx+by*map[m].bxs];	// number of mobs in the mob block
+		if( (c1==0) && (c2==0) ) continue;				// skip if nothing in the blocks
+
+//printf("block(%i,%i) %i %i\n",bx,by,c1,c2);fflush(stdout);
+		// test if the mid-point of the block is too far away
+		// so we could skip the whole block in this case 
+		v1 = (bx*BLOCK_SIZE+BLOCK_SIZE/2-xm)*(bx*BLOCK_SIZE+BLOCK_SIZE/2-xm)
+			+(by*BLOCK_SIZE+BLOCK_SIZE/2-ym)*(by*BLOCK_SIZE+BLOCK_SIZE/2-ym);
+//printf("block(%i,%i) v1=%f rd=%f\n",bx,by,v1,rd);fflush(stdout);		
+		// check for the worst case scenario
+		if(v1 > rd)	continue;
+
+		// it seems that the block is at least partially covered by the shooting range
+		// so we go into it
+		if(type==0 || type!=BL_MOB) {
+  			bl = map[m].block[bx+by*map[m].bxs];		// a block with the elements
+			for(i=0;i<c1 && bl;i++,bl=bl->next){		// go through all elements
+				if( bl && ( !type || bl->type==type ) && bl_list_count<BL_LIST_MAX )
+				{
+					// calculate the perpendicular from block xy to the straight line
+					k = kfact*(deltax*bl->x + deltay*bl->y + knorm);
+					// check if the perpendicular is within start and end of our line
+					if(k>=0 && k<=1)
+					{	// calculate the distance
+						v1 = deltax*k+x0 - bl->x;
+						v2 = deltay*k+y0 - bl->y;
+						distance = v1*v1+v2*v2;
+						// triangular shooting range
+						if( distance <= range*k )
+							bl_list[bl_list_count++]=bl;
+					}
+				}
+			}//end for elements
+		}
+
+		if(type==0 || type==BL_MOB) {
+			bl = map[m].block_mob[bx+by*map[m].bxs];	// and the mob block
+			for(i=0;i<c2 && bl;i++,bl=bl->next){
+				if(bl && bl_list_count<BL_LIST_MAX) {
+					// calculate the perpendicular from block xy to the straight line
+					k = kfact*(deltax*bl->x + deltay*bl->y + knorm);
+//printf("mob: (%i,%i) k=%f ",bl->x,bl->y, k);
+					// check if the perpendicular is within start and end of our line
+					if(k>=0 && k<=1)
+					{
+						 v1 = deltax*k+x0 - bl->x;
+						 v2 = deltay*k+y0 - bl->y;
+						 distance = v1*v1+v2*v2;
+//printf("dist: %f",distance);
+						 // triangular shooting range
+						 if( distance <= range*k )
+						 {
+//printf("  hit");
+							bl_list[bl_list_count++]=bl;
+						 }
+					}
+//printf("\n");
+				}
+			}//end for mobs
+		}
+	}//end for(bx,by)
+
+
+	if(bl_list_count>=BL_LIST_MAX) {
+		if(battle_config.error_log)
+			printf("map_foreachinarea: *WARNING* block count too many!\n");
+	}
+
+	va_start(ap,type);
+	map_freeblock_lock();	// メモリからの解放を禁止する
+
+	for(i=blockcount;i<bl_list_count;i++)
+		if(bl_list[i]->prev)	// 有?かどうかチェック
+			func(bl_list[i],ap);
+
+	map_freeblock_unlock();	// 解放を許可する
+	va_end(ap);
+	
+	bl_list_count = blockcount;
+
+*/
+
+
+//////////////////////////////////////////////////////////////
+//
+// sharp shooting 2
+//
+//////////////////////////////////////////////////////////////
+// problem: 
+// finding targets standing exactly on a line
+// (only t1 and t2 get hit)
+//
+//     target 1
+//      x t4
+//     t2
+// t3 x
+//   x
+//  S
+//////////////////////////////////////////////////////////////
+	va_list ap;
+	int i, blockcount = bl_list_count;
+	struct block_list *bl;
+	int c1,c2;
+
+	//////////////////////////////////////////////////////////////
+	// linear parametric equation
+	// x=(x1-x0)*t+x0; y=(y1-y0)*t+y0; t=[0,1]
+	//////////////////////////////////////////////////////////////
+	// linear equation for finding a single line between (x0,y0)->(x1,y1)
+	// independent of the given xy-values
+	double deltax = (x1-x0);
+	double deltay = (y1-y0);
+	double dx = 0.0;
+	double dy = 0.0;
+	int bx=-1;	// initialize block coords to some impossible value
+	int by=-1;
+
+	int t;
+	///////////////////////////////
+	// find maximum runindex
+	int tmax = abs(y1-y0);
+	if(tmax  < abs(x1-x0))	
+		tmax = abs(x1-x0);
+	// pre-calculate delta values for x and y destination
+	// should speed up cause you don't need to divide in the loop
+	if(tmax>0)
+	{
+		dx = ((double)(x1-x0)) / ((double)tmax);
+		dy = ((double)(y1-y0)) / ((double)tmax);
+	}
+	// go along the index
+	for(t=0; t<=tmax; t++)
+	{	// xy-values of the line including start and end point
+		int x = (int)floor(deltax * (double)t +0.5)+x0;
+		int y = (int)floor(deltay * (double)t +0.5)+y0;
+
+		// check the block index of the calculated xy
+		if( (bx!=x/BLOCK_SIZE) || (by!=y/BLOCK_SIZE) )
+		{	// we have reached a new block
+			// so we store the current block coordinates
+			bx = x/BLOCK_SIZE;
+			by = y/BLOCK_SIZE;
+
+			// and process the data
+			c1  = map[m].block_count[bx+by*map[m].bxs];		// number of elements in the block
+			c2  = map[m].block_mob_count[bx+by*map[m].bxs];	// number of mobs in the mob block
+			if( (c1==0) && (c2==0) ) continue;				// skip if nothing in the block
+
+			if(type==0 || type!=BL_MOB) {
+				bl = map[m].block[bx+by*map[m].bxs];		// a block with the elements
+				for(i=0;i<c1 && bl;i++,bl=bl->next){		// go through all elements
+					if( bl && ( !type || bl->type==type ) && bl_list_count<BL_LIST_MAX )
+					{	
+						// check if block xy is on the line
+						if( (bl->x-x0)*(y1-y0) == (bl->y-y0)*(x1-x0) )
+						// and if it is within start and end point
+						if( ((x0<=x1)&&(x0<=bl->x)&&(bl->x<=x1) || (x0>=x1)&&(x0>=bl->x)&&(bl->x>=x1)) &&
+							((y0<=y1)&&(y0<=bl->y)&&(bl->y<=y1) || (y0>=y1)&&(y0>=bl->y)&&(bl->y>=y1)) )
+							bl_list[bl_list_count++]=bl;
+					}
+				}//end for elements
+			}
+
+			if(type==0 || type==BL_MOB) {
+				bl = map[m].block_mob[bx+by*map[m].bxs];	// and the mob block
+				for(i=0;i<c2 && bl;i++,bl=bl->next){
+					if(bl && bl_list_count<BL_LIST_MAX) {
+						// check if mob xy is on the line
+						if( (bl->x-x0)*(y1-y0) == (bl->y-y0)*(x1-x0) )
+						// and if it is within start and end point
+						if( ((x0<=x1)&&(x0<=bl->x)&&(bl->x<=x1) || (x0>=x1)&&(x0>=bl->x)&&(bl->x>=x1)) &&
+							((y0<=y1)&&(y0<=bl->y)&&(bl->y<=y1) || (y0>=y1)&&(y0>=bl->y)&&(bl->y>=y1)) )
+							bl_list[bl_list_count++]=bl;
+					}
+				}//end for mobs
+			}	
+		}
+	}//end for index
+
+	if(bl_list_count>=BL_LIST_MAX) {
+		if(battle_config.error_log)
+			printf("map_foreachinarea: *WARNING* block count too many!\n");
+	}
+
+	va_start(ap,type);
+	map_freeblock_lock();	// メモリからの解放を禁止する
+
+	for(i=blockcount;i<bl_list_count;i++)
+		if(bl_list[i]->prev)	// 有?かどうかチェック
+			func(bl_list[i],ap);
+
+	map_freeblock_unlock();	// 解放を許可する
+	va_end(ap);
+	
+	bl_list_count = blockcount;
 }
 
 /*==========================================