瀏覽代碼

- Implemented official Convex Mirror (With help of Yomanda and Sirius White)
- Fixed SC_WARN don't receiving reflecting damage (Bug Report 1854).

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@12997 54d463be-8e91-2dee-dedb-b68131a5f0ec

zephyrus 17 年之前
父節點
當前提交
d48a482180
共有 10 個文件被更改,包括 149 次插入9 次删除
  1. 1 1
      db/item_db.txt
  2. 44 0
      src/map/clif.c
  3. 1 0
      src/map/clif.h
  4. 49 4
      src/map/map.c
  5. 2 0
      src/map/map.h
  6. 2 1
      src/map/mob.c
  7. 1 0
      src/map/mob.h
  8. 2 0
      src/map/pc.c
  9. 2 2
      src/map/skill.c
  10. 45 1
      src/map/status.c

+ 1 - 1
db/item_db.txt

@@ -3451,7 +3451,7 @@
 12211,Kafra_Card,Kafra Card,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ callfunc "F_CashStore"; },{},{}
 12212,Giant_Fly_Wing,Giant Fly Wing,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ callfunc "F_CashPartyCall"; },{},{}
 12213,Neuralizer,Neuralizer,2,,,0,,,,,0xFFFFFFFF,7,2,,,,,,{ callfunc "F_CashReset"; },{},{}
-12214,Convex_Mirror,Convex Mirror,3,20,,10,,,,,,,,,,,,,{},{},{}
+12214,Convex_Mirror,Convex Mirror,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ sc_start SC_BOSSMAPINFO,600000,0;},{},{}
 12215,Blessing_10_Scroll,LV10 Blessing Scroll,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ skilleffect "AL_BLESSING",0; sc_start SC_BLESSING,240000,10; },{},{}
 12216,Inc_Agi_10_Scroll,LV10 Agil Scroll,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ if (Hp > 15) { skilleffect "AL_INCAGI",0; sc_start SC_INCREASEAGI,240000,10; heal -15,0; } },{},{}
 12217,Aspersio_5_Scroll,LV5 Aspersio Scroll,2,,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ if (countitem(523) > 0) { skilleffect "PR_ASPERSIO",0; sc_start SC_ASPERSIO,180000,5; delitem 523,1; } },{},{}

+ 44 - 0
src/map/clif.c

@@ -12152,6 +12152,50 @@ void clif_parse_Adopt_reply(int fd, struct map_session_data *sd)
 	pc_adoption(p1_sd, p2_sd, sd);
 }
 
+/*==========================================
+ * Convex Mirror
+ * S 0293 <flag>.b <x>.l <y>.l <Hours>.w <Minutes>.w <unknown>.l <monster name>.40B <unknown>.11B
+ *==========================================*/
+void clif_bossmapinfo(int fd, struct mob_data *md, short flag)
+{
+	WFIFOHEAD(fd,70);
+	memset(WFIFOP(fd,0),0,70);
+	WFIFOW(fd,0) = 0x0293;
+
+	if( md != NULL )
+	{
+		if( md->bl.prev != NULL )
+		{ // Boss on This Map
+			if( flag )
+			{
+				WFIFOB(fd,2) = 1;
+				WFIFOL(fd,3) = md->bl.x;
+				WFIFOL(fd,7) = md->bl.y;
+			}
+			else
+				WFIFOB(fd,2) = 2; // First Time
+		}
+		else
+		{ // Boss is Dead
+			const struct TimerData * timer_data = get_timer(md->spawn_timer);
+			unsigned int seconds;
+			int hours, minutes;
+
+			seconds = DIFF_TICK(timer_data->tick, gettick()) / 1000;
+			hours = seconds / (60 * 60);
+			seconds = seconds - (60 * 60 * hours);
+			minutes = seconds / 60;
+
+			WFIFOB(fd,2) = 3;
+			WFIFOW(fd,11) = hours; // Hours
+			WFIFOW(fd,13) = minutes; // Minutes
+		}
+		safestrncpy((char*)WFIFOP(fd,19), md->db->jname, NAME_LENGTH);
+	}
+
+	WFIFOSET(fd,70);
+}
+
 /*==========================================
  * Requesting equip of a player
  *------------------------------------------*/

+ 1 - 0
src/map/clif.h

@@ -433,6 +433,7 @@ void clif_Auction_close(int fd, unsigned char flag);
 void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd);
 #endif
 
+void clif_bossmapinfo(int fd, struct mob_data *md, short flag);
 void clif_cashshop_show(struct map_session_data *sd, struct npc_data *nd);
 
 // ADOPTION

+ 49 - 4
src/map/map.c

@@ -99,6 +99,7 @@ char *GRF_PATH_FILENAME;
 static DBMap* id_db=NULL; // int id -> struct block_list*
 static DBMap* pc_db=NULL; // int id -> struct map_session_data*
 static DBMap* mobid_db=NULL; // int id -> struct mob_data*
+static DBMap* bossid_db=NULL; // int id -> struct mob_data* (MVP db)
 static DBMap* map_db=NULL; // unsigned int mapindex -> struct map_data*
 static DBMap* nick_db=NULL; // int char_id -> struct charid2nick* (requested names of offline characters)
 static DBMap* charid_db=NULL; // int char_id -> struct map_session_data*
@@ -1525,14 +1526,21 @@ void map_addiddb(struct block_list *bl)
 {
 	nullpo_retv(bl);
 
-	if (bl->type == BL_PC)
+	if( bl->type == BL_PC )
 	{
 		TBL_PC* sd = (TBL_PC*)bl;
 		idb_put(pc_db,sd->bl.id,sd);
 		idb_put(charid_db,sd->status.char_id,sd);
-	} else if (bl->type == BL_MOB)
+	}
+	else if( bl->type == BL_MOB )
+	{
+		struct mob_data* md = BL_CAST(BL_MOB, bl);
 		idb_put(mobid_db,bl->id,bl);
 
+		if( md && (md->db->status.mode&MD_BOSS) && md->db->mexp > 0 )
+			idb_put(bossid_db, bl->id, bl);
+	}
+
 	idb_put(id_db,bl->id,bl);
 }
 
@@ -1543,13 +1551,17 @@ void map_deliddb(struct block_list *bl)
 {
 	nullpo_retv(bl);
 
-	if (bl->type == BL_PC)
+	if( bl->type == BL_PC )
 	{
 		TBL_PC* sd = (TBL_PC*)bl;
 		idb_remove(pc_db,sd->bl.id);
 		idb_remove(charid_db,sd->status.char_id);
-	} else if (bl->type == BL_MOB)
+	}
+	else if( bl->type == BL_MOB )
+	{
 		idb_remove(mobid_db,bl->id);
+		idb_remove(bossid_db,bl->id);
+	}
 	idb_remove(id_db,bl->id);
 }
 
@@ -1578,6 +1590,8 @@ int map_quit(struct map_session_data *sd)
 	//(changing map-servers invokes unit_free but bypasses map_quit)
 	if(sd->sc.count) {
 		//Status that are not saved...
+		if(sd->sc.data[SC_BOSSMAPINFO])
+			status_change_end(&sd->bl,SC_BOSSMAPINFO,-1);
 		if(sd->sc.data[SC_AUTOTRADE])
 			status_change_end(&sd->bl,SC_AUTOTRADE,-1);
 		if(sd->sc.data[SC_SPURT])
@@ -1744,6 +1758,35 @@ struct block_list * map_id2bl(int id)
 	return bl;
 }
 
+/*==========================================
+ * Convext Mirror
+ *------------------------------------------*/
+struct mob_data * map_getmob_boss(int m)
+{
+	DBIterator* iter;
+	struct mob_data *md = NULL;
+	bool found = false;
+
+	iter = db_iterator(bossid_db);
+	for( md = (struct mob_data*)dbi_first(iter); dbi_exists(iter); md = (struct mob_data*)dbi_next(iter) )
+	{
+		if( md->bl.m != m || !md->spawn )
+			continue;
+
+		found = true;
+		break;
+	}
+	dbi_destroy(iter);
+
+	return (found)? md : NULL;
+}
+
+struct mob_data * map_id2boss(int id)
+{
+	if (id <= 0) return NULL;
+	return (struct mob_data*)idb_get(bossid_db,id);
+}
+
 /// Applies func to all the players in the db.
 /// Stops iterating if func returns -1.
 void map_foreachpc(int (*func)(struct map_session_data* sd, va_list args), ...)
@@ -3179,6 +3222,7 @@ void do_final(void)
 	id_db->destroy(id_db, NULL);
 	pc_db->destroy(pc_db, NULL);
 	mobid_db->destroy(mobid_db, NULL);
+	bossid_db->destroy(bossid_db, NULL);
 	nick_db->destroy(nick_db, nick_db_final);
 	charid_db->destroy(charid_db, NULL);
 
@@ -3351,6 +3395,7 @@ int do_init(int argc, char *argv[])
 	id_db = idb_alloc(DB_OPT_BASE);
 	pc_db = idb_alloc(DB_OPT_BASE);	//Added for reliable map_id2sd() use. [Skotlex]
 	mobid_db = idb_alloc(DB_OPT_BASE);	//Added to lower the load of the lazy mob ai. [Skotlex]
+	bossid_db = idb_alloc(DB_OPT_BASE); // Used for Convex Mirror quick MVP search
 	map_db = uidb_alloc(DB_OPT_BASE);
 	nick_db = idb_alloc(DB_OPT_BASE);
 	charid_db = idb_alloc(DB_OPT_BASE);

+ 2 - 0
src/map/map.h

@@ -569,6 +569,8 @@ void map_foreachmob(int (*func)(struct mob_data* md, va_list args), ...);
 void map_foreachnpc(int (*func)(struct npc_data* bl, va_list args), ...);
 void map_foreachiddb(int (*func)(struct block_list* bl, va_list args), ...);
 struct map_session_data * map_nick2sd(const char*);
+struct mob_data * map_getmob_boss(int m);
+struct mob_data * map_id2boss(int id);
 
 /// Bitfield of flags for the iterator.
 enum e_mapitflags

+ 2 - 1
src/map/mob.c

@@ -951,7 +951,7 @@ int mob_setdelayspawn(struct mob_data *md)
 	if (spawntime < 5000) //Min respawn time (is it needed?)
 		spawntime = 5000;
 
-	add_timer(gettick()+spawntime, mob_delayspawn, md->bl.id, 0);
+	md->spawn_timer = add_timer(gettick()+spawntime, mob_delayspawn, md->bl.id, 0);
 	return 0;
 }
 
@@ -1007,6 +1007,7 @@ int mob_spawn (struct mob_data *md)
 	md->attacked_id = 0;
 	md->target_id = 0;
 	md->move_fail_count = 0;
+	md->spawn_timer = -1;
 
 //	md->master_id = 0;
 	md->master_dist = 0;

+ 1 - 0
src/map/mob.h

@@ -121,6 +121,7 @@ struct mob_data {
 		unsigned flag : 1; //0: Normal. 1: Homunc exp
 	} dmglog[DAMAGELOG_SIZE];
 	struct spawn_data *spawn; //Spawn data.
+	int spawn_timer; //Required for Convex Mirror
 	struct item *lootitem;
 	short class_;
 	unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]

+ 2 - 0
src/map/pc.c

@@ -3528,6 +3528,8 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 		{ //Cancel some map related stuff.
 			if (sd->sc.data[SC_JAILED])
 				return 1; //You may not get out!
+			if (sd->sc.data[SC_BOSSMAPINFO])
+				status_change_end(&sd->bl,SC_BOSSMAPINFO,-1);
 			if (sd->sc.data[SC_WARM])
 				status_change_end(&sd->bl,SC_WARM,-1);
 			if (sd->sc.data[SC_SUN_COMFORT])

+ 2 - 2
src/map/skill.c

@@ -1471,8 +1471,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 	if( (skillid == AL_INCAGI || skillid == AL_BLESSING) && tsd->sc.data[SC_CHANGEUNDEAD] )
 		damage = 1;
 
-	if (damage > 0 && dmg.flag&BF_WEAPON && src != bl && src == dsrc &&
-		skillid != WS_CARTTERMINATION) // FIXME(?): Quick and dirty check, but HSCR does bypass Shield Reflect... so I make it bypass the whole reflect thing [DracoRPG]
+	if( damage > 0 && dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skillid == SG_SUN_WARM || skillid == SG_MOON_WARM || skillid == SG_STAR_WARM ) ) )
+		&& skillid != WS_CARTTERMINATION )
 		rdamage = battle_calc_return_damage(bl, damage, dmg.flag);
 
 	//Skill hit type

+ 45 - 1
src/map/status.c

@@ -4586,6 +4586,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 	struct view_data *vd;
 	int opt_flag, calc_flag, undead_flag;
 
+	struct mob_data *boss_md = NULL;
+
 	nullpo_retr(0, bl);
 	sc = status_get_sc(bl);
 	status = status_get_status_data(bl);
@@ -4912,6 +4914,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			break;
 			case SC_HPREGEN:
 			case SC_SPREGEN:
+			case SC_BOSSMAPINFO:
 			case SC_STUN:
 			case SC_SLEEP:
 			case SC_POISON:
@@ -5238,6 +5241,22 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 				val4 = 1;
 			tick = val2 * 1000; // val2 = Seconds between heals
 			break;
+		case SC_BOSSMAPINFO:
+			if( sd != NULL )
+			{
+				boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map
+				if( boss_md == NULL || boss_md->bl.prev == NULL )
+				{ // No MVP on this map - MVP is dead
+					clif_bossmapinfo(sd->fd, boss_md, 1);
+					return 0; // No need to start SC
+				}
+
+				val1 = boss_md->bl.id;
+				if( (val4 = tick/1000) < 1 )
+					val4 = 1;
+				tick = 1000;
+			}
+			break;
 		case SC_HIDING:
 			val2 = tick/1000;
 			tick = 1000;
@@ -6102,6 +6121,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 		if (ud)
 			ud->state.running = unit_run(bl);
 	}
+
+	if( boss_md != NULL )
+		clif_bossmapinfo(sd->fd, boss_md, 0); // First Message
+
 	return 1;
 }
 /*==========================================
@@ -6683,6 +6706,9 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr data)
 	struct status_change *sc;
 	struct status_change_entry *sce;
 
+	struct mob_data *boss_md = NULL;
+	int result;
+
 	bl = map_id2bl(id);
 	if(!bl)
 	{
@@ -6856,6 +6882,19 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr data)
 		}
 		break;
 
+	case SC_BOSSMAPINFO:
+		if( sd && --(sce->val4) >= 0 )
+		{
+			boss_md = map_id2boss(sce->val1);
+			if( boss_md && boss_md->bl.prev != NULL && sd->bl.m == boss_md->bl.m )
+			{
+				clif_bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap
+				sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+				return 0;
+			}
+		}
+		break;
+
 	case SC_DANCING: //ダンススキルの時間SP消費
 		{
 			int s = 0;
@@ -7007,7 +7046,12 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr data)
 	}
 
 	// default for all non-handled control paths is to end the status
-	return status_change_end( bl,type,tid );
+	result = status_change_end( bl,type,tid );
+	
+	if( sd && boss_md && boss_md->bl.prev == NULL )
+		clif_bossmapinfo(sd->fd, boss_md, 1); // Killed MVP - Show next spawn info
+
+	return result;
 #undef sc_timer_next
 }