From 8ac49c6058db5bf7f06662b8370b1d0fdf17d578 Mon Sep 17 00:00:00 2001 From: wushin Date: Mon, 8 Jun 2015 00:40:20 -0500 Subject: new/modified builtins areatimer foreach aggravate Override attack animation injure summon --- src/map/map.hpp | 4 + src/map/mob.cpp | 10 ++ src/map/mob.hpp | 1 + src/map/npc.cpp | 88 +++++++++++++ src/map/npc.hpp | 1 + src/map/pc.cpp | 67 +++++++++- src/map/pc.hpp | 2 + src/map/script-fun.cpp | 329 +++++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 484 insertions(+), 18 deletions(-) diff --git a/src/map/map.hpp b/src/map/map.hpp index 47eef57..a2f2ff2 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -208,6 +208,7 @@ struct map_session_data : block_list, SessionData tick_t cast_tick; // [Fate] Next tick at which spellcasting is allowed dumb_ptr active_spells; // [Fate] Singly-linked list of active spells linked to this PC BlockId attack_spell_override; // [Fate] When an attack spell is active for this player, they trigger it + NpcEvent magic_attack; // like a weapon. Check pc_attack_timer() for details. // Weapon equipment slot (slot 4) item override StatusChange attack_spell_icon_override; @@ -479,6 +480,9 @@ struct mob_data : block_list // [Fate] mob-specific stats earray stats; short size; + // Npc Runscripts + std::list eventqueuel; + Array eventtimer; }; struct BlockLists diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 7566e03..1be40f0 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -1396,6 +1396,16 @@ int mob_target(dumb_ptr md, dumb_ptr bl, int dist) return 0; } +int mob_aggravate(dumb_ptr md, dumb_ptr bl) +{ + if (md->bl_type != BL::MOB) + return 0; + mob_target(md, bl, battle_get_range(bl)); + md->target_id = bl->bl_id; + md->attacked_id = bl->bl_id; + return 1; +} + /*========================================== * The ?? routine of an active monster *------------------------------------------ diff --git a/src/map/mob.hpp b/src/map/mob.hpp index 6d87228..a466d0b 100644 --- a/src/map/mob.hpp +++ b/src/map/mob.hpp @@ -100,6 +100,7 @@ BlockId mob_once_spawn_area(dumb_ptr sd, NpcEvent event); int mob_target(dumb_ptr md, dumb_ptr bl, int dist); +int mob_aggravate(dumb_ptr md, dumb_ptr bl); int mob_stop_walking(dumb_ptr md, int type); int mob_stopattack(dumb_ptr); int mob_spawn(BlockId); diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 4296432..1707c1c 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -286,6 +286,94 @@ int npc_event_do_oninit(void) return 0; } +/*========================================== + * + *------------------------------------------ + */ +static +void npc_eventtimer(TimerData *, tick_t, BlockId id, NpcEvent data) +{ + Option> ev_ = ev_db.search(data); + dumb_ptr nd; + dumb_ptr bl = map_id2bl(id); + + if (ev_.is_none() && data.label == stringish("OnTouch"_s)) + return; + + P ev = TRY_UNWRAP(ev_, + { + if (battle_config.error_log) + PRINTF("npc_event: event not found [%s]\n"_fmt, + data); + return; + }); + if ((nd = ev->nd) == nullptr) + { + if (battle_config.error_log) + PRINTF("npc_event: event not found [%s]\n"_fmt, + data); + return; + } + + if (nd->scr.event_needs_map) + { + int xs = nd->scr.xs; + int ys = nd->scr.ys; + if (nd->bl_m != bl->bl_m) + return; + if (xs > 0 + && (bl->bl_x < nd->bl_x - xs / 2 || nd->bl_x + xs / 2 < bl->bl_x)) + return; + if (ys > 0 + && (bl->bl_y < nd->bl_y - ys / 2 || nd->bl_y + ys / 2 < bl->bl_y)) + return; + } + + run_script(ScriptPointer(borrow(*nd->scr.script), ev->pos), id, nd->bl_id); +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_addeventtimer(dumb_ptr bl, interval_t tick, NpcEvent name) +{ + int i; + + nullpo_retz(bl); + if (bl->bl_type == BL::MOB) + { + dumb_ptr md = bl->is_mob(); + for (i = 0; i < MAX_EVENTTIMER; i++) + if (!md->eventtimer[i]) + break; + + if (i < MAX_EVENTTIMER) + { + md->eventtimer[i] = Timer(gettick() + tick, + std::bind(npc_eventtimer, ph::_1, ph::_2, + md->bl_id, name)); + return 1; + } + } + if (bl->bl_type == BL::NPC) + { + dumb_ptr nd = bl->is_npc(); + for (i = 0; i < MAX_EVENTTIMER; i++) + if (!nd->eventtimer[i]) + break; + + if (i < MAX_EVENTTIMER) + { + nd->eventtimer[i] = Timer(gettick() + tick, + std::bind(npc_eventtimer, ph::_1, ph::_2, + nd->bl_id, name)); + return 1; + } + } + return 0; +} + /// Callback for npc OnTimer*: labels. /// This will be called later if you call npc_timerevent_start. /// This function may only expire, but not deactivate, the counter. diff --git a/src/map/npc.hpp b/src/map/npc.hpp index b587f5f..7a96bf9 100644 --- a/src/map/npc.hpp +++ b/src/map/npc.hpp @@ -45,6 +45,7 @@ constexpr Species INVISIBLE_CLASS = wrap(32767); int npc_event_dequeue(dumb_ptr sd); int npc_event(dumb_ptr sd, NpcEvent npcname, int); +int npc_addeventtimer(dumb_ptr bl, interval_t tick, NpcEvent name); int npc_touch_areanpc(dumb_ptr, Borrowed, int, int); int npc_click(dumb_ptr, BlockId); int npc_scriptcont(dumb_ptr, BlockId); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index c47925f..c4c3ad9 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -635,6 +635,51 @@ int pc_isequip(dumb_ptr sd, IOff0 n) return 1; } +void pc_set_weapon_icon(dumb_ptr sd, int count, + StatusChange icon, ItemNameId look) +{ + const StatusChange old_icon = sd->attack_spell_icon_override; + + sd->attack_spell_icon_override = icon; + sd->attack_spell_look_override = look; + + if (old_icon != StatusChange::ZERO && old_icon != icon) + clif_status_change(sd, old_icon, 0); + + clif_fixpcpos(sd); + if (count) + { + clif_changelook(sd, LOOK::WEAPON, unwrap(look)); + if (icon != StatusChange::ZERO) + clif_status_change(sd, icon, 1); + } + else + { + /* Set it to `normal' */ + clif_changelook(sd, LOOK::WEAPON, + static_cast(sd->status.weapon)); + } +} + +void pc_set_attack_info(dumb_ptr sd, interval_t speed, int range) +{ + sd->attack_spell_delay = speed; + sd->attack_spell_range = range; + + if (speed == interval_t::zero()) + { + pc_calcstatus(sd, 1); + clif_updatestatus(sd, SP::ASPD); + clif_updatestatus(sd, SP::ATTACKRANGE); + } + else + { + sd->aspd = speed; + clif_updatestatus(sd, SP::ASPD); + clif_updatestatus(sd, SP::ATTACKRANGE); + } +} + /*========================================== * session idに問題無し * char鯖から送られてきたステータスを設定 @@ -2608,13 +2653,23 @@ void pc_attack_timer(TimerData *, tick_t tick, BlockId id) if (sd->attackabletime > tick) return; // cannot attack yet - interval_t attack_spell_delay = sd->attack_spell_delay; - if (sd->attack_spell_override // [Fate] If we have an active attack spell, use that - && magic::spell_attack(id, sd->attacktarget)) + if (sd->attack_spell_override) // [Fate] If we have an active attack spell, use that { - // Return if the spell succeeded. If the spell had disspiated, spell_attack() may fail. - sd->attackabletime = tick + attack_spell_delay; - + // call_spell_event_script + argrec_t arg[1] = + { + {"@target_id"_s, static_cast(unwrap(bl->bl_id))}, + }; + npc_event_do_l(sd->magic_attack, sd->bl_id, arg); + sd->attackabletime = tick + sd->attack_spell_delay; + sd->attack_spell_charges--; + if (!sd->attack_spell_charges) + { + sd->attack_spell_override = BlockId(); + pc_set_weapon_icon(sd, 0, StatusChange::ZERO, ItemNameId()); + pc_set_attack_info(sd, interval_t::zero(), 0); + pc_calcstatus(sd, 0); + } } else { diff --git a/src/map/pc.hpp b/src/map/pc.hpp index db1d5be..3a35330 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -80,6 +80,8 @@ int pc_counttargeted(dumb_ptr sd, dumb_ptr src, int pc_setrestartvalue(dumb_ptr sd, int type); void pc_makesavestatus(dumb_ptr); int pc_setnewpc(dumb_ptr, AccountId, CharId, int, uint32_t /*tick_t*/, SEX); +void pc_set_weapon_icon(dumb_ptr sd, int count,StatusChange icon, ItemNameId look); +void pc_set_attack_info(dumb_ptr sd, interval_t speed, int range); int pc_authok(AccountId, int, ClientVersion, const CharKey *, const CharData *); int pc_authfail(AccountId accid); diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 07076d5..9f9652e 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -70,6 +70,13 @@ namespace map #define AARG(n) (st->stack->stack_datav[st->start + 2 + (n)]) #define HARG(n) (st->end > st->start + 2 + (n)) +enum class MonsterAttitude +{ + HOSTILE = 0, + FRIENDLY = 1, + SERVANT = 2, + FROZEN = 3, +}; // // 埋め込み関数 // @@ -498,6 +505,81 @@ void builtin_heal(ScriptState *st) pc_heal(sd, hp, sp); } +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_elttype(ScriptState *st) +{ + int element_type = static_cast(battle_get_element(map_id2bl(wrap(conv_num(st, &AARG(0))))).element); + push_int(st->stack, element_type); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_eltlvl(ScriptState *st) +{ + int element_lvl = static_cast(battle_get_element(map_id2bl(wrap(conv_num(st, &AARG(0))))).level); + push_int(st->stack, element_lvl); +} + +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_injure(ScriptState *st) +{ + dumb_ptr caster = map_id2bl(st->rid); + dumb_ptr target = map_id2bl(wrap(conv_num(st, &AARG(0)))); + int damage_caused = conv_num(st, &AARG(1)); + int mp_damage = conv_num(st, &AARG(2)); + int target_hp = battle_get_hp(target); + int mdef = battle_get_mdef(target); + + if (target->bl_type == BL::PC + && !target->bl_m->flag.get(MapFlag::PVP) + && (caster->bl_type == BL::PC) + && ((caster->is_player()->state.pvpchannel > 1) && (target->is_player()->state.pvpchannel != caster->is_player()->state.pvpchannel))) + return; /* Cannot damage other players outside of pvp */ + + if (target != caster) + { + /* Not protected against own spells */ + damage_caused = (damage_caused * (100 - mdef)) / 100; + mp_damage = (mp_damage * (100 - mdef)) / 100; + } + + damage_caused = (damage_caused > target_hp) ? target_hp : damage_caused; + + if (damage_caused < 0) + damage_caused = 0; + + // display damage first, because dealing damage may deallocate the target. + clif_damage(caster, target, + gettick(), interval_t::zero(), interval_t::zero(), + damage_caused, 0, DamageType::NORMAL); + + if (caster->bl_type == BL::PC) + { + dumb_ptr caster_pc = caster->is_player(); + if (target->bl_type == BL::MOB) + { + dumb_ptr mob = target->is_mob(); + dumb_ptr nd = map_id_is_npc(st->oid); + MAP_LOG_PC(caster_pc, "SPELLDMG MOB%d %d FOR %d BY %s"_fmt, + mob->bl_id, mob->mob_class, damage_caused, nd->name); + } + } + battle_damage(caster, target, damage_caused, mp_damage); + + return; +} + /*========================================== * *------------------------------------------ @@ -639,6 +721,61 @@ void builtin_elif (ScriptState *st) run_func(st); } +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_foreach_sub(dumb_ptr bl, NpcEvent event, BlockId caster) +{ + // call_spell_event_script + argrec_t arg[1] = + { + {"@target_id"_s, static_cast(unwrap(bl->bl_id))}, + }; + npc_event_do_l(event, caster, arg); +} +static +void builtin_foreach(ScriptState *st) +{ + int x0, y0, x1, y1, bl_num; + + dumb_ptr caster = map_id2bl(st->rid); + bl_num = conv_num(st, &AARG(0)); + MapName mapname = stringish(ZString(conv_str(st, &AARG(1)))); + x0 = conv_num(st, &AARG(2)); + y0 = conv_num(st, &AARG(3)); + x1 = conv_num(st, &AARG(4)); + y1 = conv_num(st, &AARG(5)); + ZString event_ = ZString(conv_str(st, &AARG(6))); + BL block_type; + NpcEvent event; + extract(event_, &event); + + P m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + + switch (bl_num) + { + case 0: + block_type = BL::PC; + break; + case 1: + block_type = BL::NPC; + break; + case 2: + block_type = BL::MOB; + break; + default: + return; + } + + map_foreachinarea(std::bind(builtin_foreach_sub, ph::_1, event, caster->bl_id), + m, + x0, y0, + x1, y1, + block_type); +} + /*========================================== * 変数設定 *------------------------------------------ @@ -1396,6 +1533,31 @@ void builtin_getskilllv(ScriptState *st) push_int(st->stack, pc_checkskill(script_rid2sd(st), id)); } +/*========================================== + * + *------------------------------------------ + */ +static +void builtin_overrideattack(ScriptState *st) +{ + dumb_ptr sd = script_rid2sd(st); + int charges = conv_num(st, &AARG(0)); + interval_t attack_delay = static_cast(conv_num(st, &AARG(1))); + int attack_range = conv_num(st, &AARG(2)); + StatusChange icon = StatusChange(conv_num(st, &AARG(3))); + ItemNameId look = wrap(static_cast(conv_num(st, &AARG(4)))); + ZString event_ = ZString(conv_str(st, &AARG(5))); + + NpcEvent event; + extract(event_, &event); + + sd->attack_spell_override = st->oid; + sd->attack_spell_charges = charges; + sd->magic_attack = event; + pc_set_weapon_icon(sd, charges, icon, look); + pc_set_attack_info(sd, attack_delay, attack_range); +} + /*========================================== * *------------------------------------------ @@ -1594,6 +1756,72 @@ void builtin_getexp(ScriptState *st) } +static +void builtin_summon(ScriptState *st) +{ + NpcEvent event; + MapName map = stringish(ZString(conv_str(st, &AARG(0)))); + int x = conv_num(st, &AARG(1)); + int y = conv_num(st, &AARG(2)); + dumb_ptr owner_e = map_id2bl(wrap(conv_num(st, &AARG(3)))); + dumb_ptr owner = nullptr; + Species monster_id = wrap(conv_num(st, &AARG(4))); + MonsterAttitude monster_attitude = static_cast(conv_num(st, &AARG(5))); + interval_t lifespan = static_cast(conv_num(st, &AARG(6))); + if (HARG(7)) + extract(ZString(conv_str(st, &AARG(7))), &event); + + if (monster_attitude == MonsterAttitude::SERVANT + && owner_e->bl_type == BL::PC) + owner = owner_e->is_player(); // XXX in the future this should also work with mobs as owner + + BlockId mob_id = mob_once_spawn(owner, map, x, y, MobName(), monster_id, 1, event); + dumb_ptr mob = map_id_is_mob(mob_id); + + if (mob) + { + mob->mode = get_mob_db(monster_id).mode; + + switch (monster_attitude) + { + case MonsterAttitude::SERVANT: + mob->state.special_mob_ai = 1; + mob->mode |= MobMode::AGGRESSIVE; + break; + + case MonsterAttitude::FRIENDLY: + mob->mode = MobMode::CAN_ATTACK | (mob->mode & MobMode::CAN_MOVE); + break; + + case MonsterAttitude::HOSTILE: + mob->mode = MobMode::CAN_ATTACK | MobMode::AGGRESSIVE | (mob->mode & MobMode::CAN_MOVE); + if (owner) + { + mob->target_id = owner->bl_id; + mob->attacked_id = owner->bl_id; + } + break; + + case MonsterAttitude::FROZEN: + mob->mode = MobMode::ZERO; + break; + } + + mob->mode |= + MobMode::SUMMONED | MobMode::TURNS_AGAINST_BAD_MASTER; + + mob->deletetimer = Timer(gettick() + lifespan, + std::bind(mob_timer_delete, ph::_1, ph::_2, + mob_id)); + + if (owner) + { + mob->master_id = owner->bl_id; + mob->master_dist = 6; + } + } +} + /*========================================== * モンスター発生 *------------------------------------------ @@ -1981,6 +2209,38 @@ void builtin_getmapusers(ScriptState *st) push_int(st->stack, users); } +static +void builtin_aggravate_sub(dumb_ptr bl, dumb_ptr target, int effect) +{ + dumb_ptr md = bl->is_mob(); + + if (mob_aggravate(md, target)) + clif_misceffect(bl, effect); +} + +static +void builtin_aggravate(ScriptState *st) +{ + dumb_ptr target = map_id2bl(st->rid); + MapName str = stringish(ZString(conv_str(st, &AARG(0)))); + int x0 = conv_num(st, &AARG(1)); + int y0 = conv_num(st, &AARG(2)); + int x1 = conv_num(st, &AARG(3)); + int y1 = conv_num(st, &AARG(4)); + int effect = conv_num(st, &AARG(5)); + P m = TRY_UNWRAP(map_mapname2mapid(str), + { + push_int(st->stack, -1); + return; + }); + + map_foreachinarea(std::bind(builtin_aggravate_sub, ph::_1, target, effect), + m, + x0, y0, + x1, y1, + BL::MOB); +} + /*========================================== * エリア指定ユーザー数所得 *------------------------------------------ @@ -3068,6 +3328,18 @@ void builtin_npctalk(ScriptState *st) clif_message(nd, str); } +/*========================================== + * casttime + *------------------------------------------ + */ +static +void builtin_casttime(ScriptState *st) +{ + dumb_ptr sd = script_rid2sd(st); + interval_t tick = static_cast(conv_num(st, &AARG(0))); + sd->cast_tick = gettick() + tick; +} + /*========================================== * getlook char info. getlook(arg) *------------------------------------------ @@ -3152,31 +3424,56 @@ void builtin_getsavepoint(ScriptState *st) static void builtin_areatimer_sub(dumb_ptr bl, interval_t tick, NpcEvent event) { - pc_addeventtimer(bl->is_player(), tick, event); + if (bl->bl_type == BL::PC) + { + dumb_ptr sd = map_id_is_player(bl->bl_id); + pc_addeventtimer(sd, tick, event); + } + else + { + npc_addeventtimer(bl, tick, event); + } } static void builtin_areatimer(ScriptState *st) { - int x0, y0, x1, y1; - - MapName mapname = stringish(ZString(conv_str(st, &AARG(0)))); - x0 = conv_num(st, &AARG(1)); - y0 = conv_num(st, &AARG(2)); - x1 = conv_num(st, &AARG(3)); - y1 = conv_num(st, &AARG(4)); - interval_t tick = static_cast(conv_num(st, &AARG(5))); - ZString event_ = ZString(conv_str(st, &AARG(6))); + int x0, y0, x1, y1, bl_num; + + bl_num = conv_num(st, &AARG(0)); + MapName mapname = stringish(ZString(conv_str(st, &AARG(1)))); + x0 = conv_num(st, &AARG(2)); + y0 = conv_num(st, &AARG(3)); + x1 = conv_num(st, &AARG(4)); + y1 = conv_num(st, &AARG(5)); + interval_t tick = static_cast(conv_num(st, &AARG(6))); + ZString event_ = conv_str(st, &AARG(7)); + BL block_type; NpcEvent event; extract(event_, &event); P m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + switch (bl_num) + { + case 0: + block_type = BL::PC; + break; + case 1: + block_type = BL::NPC; + break; + case 2: + block_type = BL::MOB; + break; + default: + return; + } + map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event), m, x0, y0, x1, y1, - BL::PC); + block_type); } /*========================================== @@ -3431,6 +3728,9 @@ BuiltinFunction builtin_functions[] = BUILTIN(warp, "Mxy"_s, '\0'), BUILTIN(areawarp, "MxyxyMxy"_s, '\0'), BUILTIN(heal, "ii?"_s, '\0'), + BUILTIN(elttype, "i"_s, 'i'), + BUILTIN(eltlvl, "i"_s, 'i'), + BUILTIN(injure, "iii"_s, '\0'), BUILTIN(input, "N"_s, '\0'), BUILTIN(if, "iF*"_s, '\0'), BUILTIN(elif, "iF*"_s, '\0'), @@ -3456,6 +3756,7 @@ BuiltinFunction builtin_functions[] = BUILTIN(skill, "ii?"_s, '\0'), BUILTIN(setskill, "ii"_s, '\0'), BUILTIN(getskilllv, "i"_s, 'i'), + BUILTIN(overrideattack, "iiiiiE"_s, '\0'), BUILTIN(getgmlevel, ""_s, 'i'), BUILTIN(end, ""_s, '\0'), BUILTIN(getopt2, ""_s, 'i'), @@ -3465,6 +3766,7 @@ BuiltinFunction builtin_functions[] = BUILTIN(gettime, "i"_s, 'i'), BUILTIN(openstorage, ""_s, '\0'), BUILTIN(getexp, "ii"_s, '\0'), + BUILTIN(summon, "Mxysmii?"_s, '\0'), BUILTIN(monster, "Mxysmi?"_s, '\0'), BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'), BUILTIN(killmonster, "ME"_s, '\0'), @@ -3525,6 +3827,7 @@ BuiltinFunction builtin_functions[] = BUILTIN(npcareawarp, "xyxyis"_s, '\0'), BUILTIN(message, "Ps"_s, '\0'), BUILTIN(npctalk, "ss?"_s, '\0'), + BUILTIN(casttime, "i"_s, '\0'), BUILTIN(title, "s"_s, '\0'), BUILTIN(smsg, "e??"_s, '\0'), BUILTIN(remotecmd, "s?"_s, '\0'), @@ -3533,11 +3836,13 @@ BuiltinFunction builtin_functions[] = BUILTIN(getmask, ""_s, 'i'), BUILTIN(getlook, "i"_s, 'i'), BUILTIN(getsavepoint, "i"_s, '.'), - BUILTIN(areatimer, "MxyxytE"_s, '\0'), + BUILTIN(areatimer, "MxyxytEi"_s, '\0'), + BUILTIN(foreach, "iMxyxyE"_s, '\0'), BUILTIN(isin, "Mxyxy"_s, 'i'), BUILTIN(iscollision, "Mxy"_s, 'i'), BUILTIN(shop, "s"_s, '\0'), BUILTIN(isdead, ""_s, 'i'), + BUILTIN(aggravate, "Mxyxyi"_s, '\0'), BUILTIN(fakenpcname, "ssi"_s, '\0'), BUILTIN(getx, ""_s, 'i'), BUILTIN(gety, ""_s, 'i'), -- cgit v1.2.3-60-g2f50