summaryrefslogtreecommitdiff
path: root/src/map/skill.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/skill.c')
-rw-r--r--src/map/skill.c170
1 files changed, 111 insertions, 59 deletions
diff --git a/src/map/skill.c b/src/map/skill.c
index be899d0dc..55bf30338 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -2089,7 +2089,7 @@ int skill_blown(struct block_list* src, struct block_list* target, int count, in
case BL_MOB:
{
const struct mob_data *md = BL_UCCAST(BL_MOB, target);
- if (md->class_ == MOBID_EMPELIUM)
+ if (md->status.mode&MD_NOKNOCKBACK)
return 0;
if (src != target && is_boss(target)) // Bosses can't be knocked-back
return 0;
@@ -2833,7 +2833,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr
if ( ssc->data[SC_POISONINGWEAPON]->val1 == 9 )// Oblivion Curse gives a 2nd success chance after the 1st one passes which is reducible. [Rytech]
rate = 100 - tstatus->int_ * 4 / 5;
sc_start(src, bl,ssc->data[SC_POISONINGWEAPON]->val2,rate,ssc->data[SC_POISONINGWEAPON]->val1,skill->get_time2(GC_POISONINGWEAPON,1) - (tstatus->vit + tstatus->luk) / 2 * 1000);
- status_change_end(src,SC_POISONINGWEAPON,-1);
+ status_change_end(src, SC_POISONINGWEAPON, INVALID_TIMER);
clif->skill_nodamage(src,bl,skill_id,skill_lv,1);
}
}
@@ -5123,7 +5123,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) {
unit->set_walkdelay(src, tick, battle_config.default_walk_delay+skill->get_walkdelay(ud->skill_id, ud->skill_lv), 1);
if(battle_config.skill_log && battle_config.skill_log&src->type)
- ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n",
+ ShowInfo("Type %u, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n",
src->type, src->id, ud->skill_id, ud->skill_lv, target->id);
map->freeblock_lock();
@@ -5401,12 +5401,12 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
type = status->skill2sc(skill_id);
tsc = status->get_sc(bl);
- tsce = (tsc && type != -1)?tsc->data[type]:NULL;
+ tsce = (tsc != NULL && type != SC_NONE) ? tsc->data[type] : NULL;
- if (src!=bl && type > -1 &&
- (element = skill->get_ele(skill_id, skill_lv)) > ELE_NEUTRAL &&
- skill->get_inf(skill_id) != INF_SUPPORT_SKILL &&
- battle->attr_fix(NULL, NULL, 100, element, tstatus->def_ele, tstatus->ele_lv) <= 0)
+ if (src != bl && type > SC_NONE
+ && (element = skill->get_ele(skill_id, skill_lv)) > ELE_NEUTRAL
+ && skill->get_inf(skill_id) != INF_SUPPORT_SKILL
+ && battle->attr_fix(NULL, NULL, 100, element, tstatus->def_ele, tstatus->ele_lv) <= 0)
return 1; //Skills that cause an status should be blocked if the target element blocks its element.
map->freeblock_lock();
@@ -7297,7 +7297,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
case NPC_REBIRTH:
if( md && md->state.rebirth )
break; // only works once
- sc_start(src,bl,type,100,skill_lv,-1);
+ sc_start(src, bl, type, 100, skill_lv, INFINITE_DURATION);
break;
case NPC_DARKBLESSING:
@@ -8648,7 +8648,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
break;
}
- sc_start(src, bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook.
+ sc_start(src, bl, SC_STOP, 100, skill_lv, INFINITE_DURATION); //Can't move while selecting a spellbook.
clif->spellbook_list(sd);
clif->skill_nodamage(src, bl, skill_id, skill_lv, 1);
}
@@ -8777,7 +8777,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
if( sd ) {
int idx1 = skill->get_index(sd->reproduceskill_id), idx2 = skill->get_index(sd->cloneskill_id);
if( sd->status.skill[idx1].id || sd->status.skill[idx2].id ) {
- sc_start(src,src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax]
+ sc_start(src, src, SC_STOP, 100, skill_lv, INFINITE_DURATION); // The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax]
clif->autoshadowspell_list(sd);
clif->skill_nodamage(src,bl,skill_id,1,1);
}
@@ -8894,7 +8894,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
splashrange = 3;
switch( opt ) {
case 1:
- sc_start(src,bl,SC_SHIELDSPELL_DEF,100,opt,INVALID_TIMER); //Splash AoE ATK
+ sc_start(src, bl, SC_SHIELDSPELL_DEF, 100, opt, INFINITE_DURATION); // Splash AoE ATK
clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL);
map->foreachinrange(skill->area_sub,src,splashrange,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id);
status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER);
@@ -8921,7 +8921,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
splashrange = 3;
switch( opt ) {
case 1:
- sc_start(src,bl,SC_SHIELDSPELL_MDEF,100,opt,INVALID_TIMER); //Splash AoE MATK
+ sc_start(src, bl, SC_SHIELDSPELL_MDEF, 100, opt, INFINITE_DURATION); // Splash AoE MATK
clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL);
map->foreachinrange(skill->area_sub,src,splashrange,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id);
status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER);
@@ -8958,7 +8958,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
sc_start(src,bl,SC_SCRESIST,100,rate,shield->refine * 30000));
break;
case 3:
- sc_start(src,bl,SC_SHIELDSPELL_REF,100,opt,INVALID_TIMER); //HP Recovery
+ sc_start(src, bl, SC_SHIELDSPELL_REF, 100, opt, INFINITE_DURATION); // HP Recovery
val = sstatus->max_hp * ((status->get_lv(src) / 10) + (shield->refine + 1)) / 100;
status->heal(bl, val, 0, 2);
status_change_end(bl,SC_SHIELDSPELL_REF,INVALID_TIMER);
@@ -9431,7 +9431,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
case SO_EL_CONTROL:
if( sd ) {
- int mode = EL_MODE_PASSIVE; // Standard mode.
+ uint32 mode = EL_MODE_PASSIVE; // Standard mode.
if( !sd->ed ) break;
@@ -9443,7 +9443,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
case 2: mode = EL_MODE_ASSIST; break;
case 3: mode = EL_MODE_AGGRESSIVE; break;
}
- if( !elemental->change_mode(sd->ed,mode) ) {
+ if (!elemental->change_mode(sd->ed, mode)) {
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
break;
}
@@ -10023,7 +10023,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data)
}
if(battle_config.skill_log && battle_config.skill_log&src->type)
- ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n",
+ ShowInfo("Type %u, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n",
src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly);
if (ud->walktimer != INVALID_TIMER)
@@ -10267,7 +10267,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
sc = status->get_sc(src);
type = status->skill2sc(skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
+ sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL;
switch (skill_id) { //Skill effect.
case WZ_METEOR:
@@ -11670,7 +11670,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick
return 0;
type = status->skill2sc(sg->skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
+ sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL;
skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still.
switch (sg->unit_id) {
case UNT_SPIDERWEB:
@@ -11777,6 +11777,10 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick
sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
break;
case UNT_APPLEIDUN:
+ // If Aegis, apple of idun doesn't update its effect
+ if (!battle_config.song_timer_reset && sc && sce)
+ return 0;
+ // Let it fall through
case UNT_WHISTLE:
case UNT_ASSASSINCROSS:
case UNT_POEMBRAGI:
@@ -11784,19 +11788,34 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick
case UNT_DONTFORGETME:
case UNT_FORTUNEKISS:
case UNT_SERVICEFORYOU:
+ // Don't buff themselves without link!
if (sg->src_id==bl->id && !(sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))
return 0;
if (!sc) return 0;
if (!sce)
sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
+ // From here songs are already active
else if (battle_config.song_timer_reset && sce->val4 == 1) {
- //Readjust timers since the effect will not last long.
+ // eA style:
+ // Readjust timers since the effect will not last long.
sce->val4 = 0;
timer->delete(sce->timer, status->change_timer);
sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type);
+ } else if (!battle_config.song_timer_reset) {
+ // Aegis style:
+ // Songs won't renew unless finished
+ const struct TimerData *td = timer->get(sce->timer);
+ if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) {
+ // Update with new values as the current one will vanish soon
+ timer->delete(sce->timer, status->change_timer);
+ sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type);
+ sce->val1 = sg->skill_lv; // Why are we storing skill_lv as val1?
+ sce->val2 = sg->val1;
+ sce->val3 = sg->val2;
+ sce->val4 = 0;
+ }
}
-
break;
case UNT_FOGWALL:
@@ -11931,7 +11950,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
case UNT_MANHOLE:
return 0;
default:
- ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id);
+ ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", (unsigned int)sg->unit_id);
return 0;
}
}
@@ -12201,18 +12220,25 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
if (md && md->class_ == MOBID_EMPELIUM)
break;
#endif
- if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))
- || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1))
+ // Don't buff themselves!
+ if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)))
break;
- heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true);
- if( tsc && tsc->data[SC_AKAITSUKI] && heal )
- heal = ~heal + 1;
- clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
- status->heal(bl, heal, 0, 0);
-
- if (!battle_config.song_timer_reset)
+ // Aegis style
+ // Check if the remaining time is enough to survive the next update
+ if (!battle_config.song_timer_reset
+ && !(tsc && tsc->data[type] && tsc->data[type]->val4 == 1)) {
+ // Apple of Idun is not active. Start it now
sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit);
+ }
+
+ if (tstatus->hp < tstatus->max_hp) {
+ heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true);
+ if( tsc && tsc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ status->heal(bl, heal, 0, 0);
+ }
}
break;
case UNT_POEMBRAGI:
@@ -12222,12 +12248,30 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
case UNT_DONTFORGETME:
case UNT_FORTUNEKISS:
case UNT_SERVICEFORYOU:
- if (battle_config.song_timer_reset
- || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1)
- || (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))
- )
+ // eA style: doesn't need this
+ if (battle_config.song_timer_reset)
+ break;
+ // Don't let buff themselves!
+ if (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))
break;
+ // Aegis style
+ // Check if song has enough time to survive the next check
+ if (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1) {
+ const struct TimerData *td = timer->get(tsc->data[type]->timer);
+ if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) {
+ // Update with new values as the current one will vanish
+ timer->delete(tsc->data[type]->timer, status->change_timer);
+ tsc->data[type]->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type);
+ tsc->data[type]->val1 = sg->skill_lv;
+ tsc->data[type]->val2 = sg->val1;
+ tsc->data[type]->val3 = sg->val2;
+ tsc->data[type]->val4 = 0;
+ }
+ break; // Had enough time or not, it now has. Exit
+ }
+
+ // Song was not active. Start it now
sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit);
break;
case UNT_TATAMIGAESHI:
@@ -12624,7 +12668,7 @@ int skill_unit_onout(struct skill_unit *src, struct block_list *bl, int64 tick)
nullpo_ret(sg=src->group);
sc = status->get_sc(bl);
type = status->skill2sc(sg->skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
+ sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL;
if( bl->prev == NULL
|| (status->isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB && sg->unit_id != UNT_THORNS_TRAP)
@@ -12688,7 +12732,7 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) {
sc = NULL;
type = status->skill2sc(skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
+ sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL;
switch (skill_id) {
case WZ_QUAGMIRE:
@@ -12749,8 +12793,9 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) {
case DC_DONTFORGETME:
case DC_FORTUNEKISS:
case DC_SERVICEFORYOU:
- if ((battle_config.song_timer_reset && sce) // athena style
- || (!battle_config.song_timer_reset && sce && sce->val4 != 1)
+
+ if ((battle_config.song_timer_reset && sce) // eAthena style: update everytime
+ || (!battle_config.song_timer_reset && sce && sce->val4 != 1) // Aegis style: update only when it was not a reduced effect
) {
timer->delete(sce->timer, status->change_timer);
//NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
@@ -16474,7 +16519,7 @@ struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list
}
if (j == -1) {
- ShowWarning ("skill_unitgrouptickset_search: tickset is full. ( failed for skill '%s' on unit %d )\n",skill->get_name(group->skill_id),bl->type);
+ ShowWarning ("skill_unitgrouptickset_search: tickset is full. ( failed for skill '%s' on unit %u )\n", skill->get_name(group->skill_id), bl->type);
j = id % MAX_SKILLUNITGROUPTICKSET;
}
@@ -17680,7 +17725,7 @@ int skill_poisoningweapon( struct map_session_data *sd, int nameid) {
return 0;
}
- status_change_end(&sd->bl, SC_POISONINGWEAPON, -1);//Status must be forced to end so that a new poison will be applied if a player decides to change poisons. [Rytech]
+ status_change_end(&sd->bl, SC_POISONINGWEAPON, INVALID_TIMER); // Status must be forced to end so that a new poison will be applied if a player decides to change poisons. [Rytech]
chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv
sc_start4(&sd->bl, &sd->bl, SC_POISONINGWEAPON, 100, pc->checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1
type, chance, 0, skill->get_time(GC_POISONINGWEAPON, sd->menuskill_val));
@@ -17800,13 +17845,13 @@ int skill_spellbook (struct map_session_data *sd, int nameid) {
for(i = SC_SPELLBOOK7; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett]
if( !sc->data[i] ){
sc->data[SC_READING_SB]->val2 += point; // increase points
- sc_start4(&sd->bl,&sd->bl, (sc_type)i, 100, skill_id, pc->checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ sc_start4(&sd->bl, &sd->bl, (sc_type)i, 100, skill_id, pc->checkskill(sd, skill_id), point, 0, INFINITE_DURATION);
break;
}
}
- }else{
- sc_start2(&sd->bl,&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER);
- sc_start4(&sd->bl,&sd->bl, SC_SPELLBOOK7, 100, skill_id, pc->checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ } else {
+ sc_start2(&sd->bl, &sd->bl, SC_READING_SB, 100, 0, point, INFINITE_DURATION);
+ sc_start4(&sd->bl, &sd->bl, SC_SPELLBOOK7, 100, skill_id, pc->checkskill(sd, skill_id), point, 0, INFINITE_DURATION);
}
return 1;
@@ -17834,27 +17879,31 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) {
sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill->get_time(SC_AUTOSHADOWSPELL,aslvl));
return 0;
}
-int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) {
+
+int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const struct itemlist *item_list)
+{
int i;
nullpo_ret(sd);
nullpo_ret(item_list);
- if( n <= 0 )
+ if (VECTOR_LENGTH(*item_list) <= 0)
return 1;
- for (i = 0; i < n; i++) {
- int nameid, add_amount, del_amount, idx, product;
+ for (i = 0; i < VECTOR_LENGTH(*item_list); i++) {
struct item tmp_item;
-
- idx = item_list[i*2+0]-2;
- del_amount = item_list[i*2+1];
+ const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+ int nameid, add_amount, product;
+ int del_amount = entry->amount;
+ int idx = entry->id;
if( skill_lv == 2 )
del_amount -= (del_amount % 10);
add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ;
- if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
+ if (idx < 0 || idx >= MAX_INVENTORY
+ || (nameid = sd->status.inventory[idx].nameid) <= 0
+ || del_amount < 0 || del_amount > sd->status.inventory[idx].amount) {
clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
return 1;
}
@@ -17904,7 +17953,8 @@ int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv,
return 0;
}
-int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
+int skill_changematerial(struct map_session_data *sd, const struct itemlist *item_list)
+{
int i, j, k, c, p = 0, nameid, amount;
nullpo_ret(sd);
@@ -17919,11 +17969,13 @@ int skill_changematerial(struct map_session_data *sd, int n, unsigned short *ite
// Verification of overlap between the objects required and the list submitted.
for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
if( skill->dbs->produce_db[i].mat_id[j] > 0 ) {
- for( k = 0; k < n; k++ ) {
- int idx = item_list[k*2+0]-2;
+ for (k = 0; k < VECTOR_LENGTH(*item_list); k++) {
+ const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, k);
+ int idx = entry->id;
+ Assert_ret(idx >= 0 && idx < MAX_INVENTORY);
+ amount = entry->amount;
nameid = sd->status.inventory[idx].nameid;
- amount = item_list[k*2+1];
- if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ if (nameid > 0 && sd->status.inventory[idx].identify == 0) {
clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_ITEM_NEED_IDENTIFY);
return 0;
}
@@ -17936,7 +17988,7 @@ int skill_changematerial(struct map_session_data *sd, int n, unsigned short *ite
break; // No more items required
}
p++;
- } while(n == j && c == n);
+ } while (c == j && VECTOR_LENGTH(*item_list) == c);
p--;
if ( p > 0 ) {
skill->produce_mix(sd,GN_CHANGEMATERIAL,skill->dbs->produce_db[i].nameid,0,0,0,p);