怪物攻城活动

需求:

定时自动开启活动, 活动倒计时30min后或完成防守目标后自动结束.

活动分为三阶段:

一阶段: 击杀牛头统帅可以提升活动难度等级, 共0牛~4牛5个难度.

0牛: 副本难度普通, 怪物等级与自身等级相同, 击杀奖励1pt

1牛: 副本难度冒险, 怪物等级100, 击杀奖励2pt

2牛: 副本难度王者, 怪物等级110, 随机刷新紫名精英怪, 击杀奖励4pt

3牛: 副本难度地狱, 怪物等级120, 随机刷新不灭粉名怪, 怪物数量*2, 击杀奖励8pt

4牛: 副本难度英雄, 怪物等级127, 随机刷新橙名英雄怪, 怪物数量*4, 击杀奖励16pt

频道总分达到100pt进入P2

二阶段: 击杀怪物只增加个人pt, 不再增加频道总pt

1min内连续击杀3只相同攻城怪物总pt+33.

城镇内若存在GBL教主教, 则定期扣除总pt. 数量越多扣除速度越快.

频道总分达到200pt进入P3

三阶段: 击杀怪物只增加个人pt, 不再增加频道总pt

击杀攻城怪物后随机刷新世界BOSS机械牛, 同一时间最多存在一只世界BOSS.

每次成功击杀世界BOSS频道总pt+25, 个人额外获得1000~5000pt

除GBL教主教外, 世界BOSS也将定期扣除总pt.

挑战世界BOSS时副本内随机刷新多只机械牛, 频道总pt越高, 机械牛数量越多.

频道总分达到300pt怪物攻城防守成功, 活动立即结束.

每次成功击杀攻城怪物, 随机获取0%-10%当前等级升级所需经验.

防守成功后将根据活动难度派发奖励, 奖励包括金币+士气冲天+天堂痊愈药剂+复活币+绝望之塔通关奖章.

全体在线玩家绝望之塔当前挑战层数设置为100.

全体在线玩家随机一件穿戴中的装备强化增幅等级根据活动难度提高+1-+5级.

所有参与活动角色获取个人pt*10点券奖励, 排行榜第一获取双倍奖励.

防守失败全体在线玩家被怪物掠夺1%-10%所持金币, 有概率被怪物抢走一件穿戴中的装备.

//初始化数据库(打开数据库/建库建表/数据库字段扩展)
function init_db()
{
//配置文件
var config = global_config[‘db_config’];

//打开数据库连接
if(mysql_taiwan_cain == null)
{
mysql_taiwan_cain = api_MYSQL_open(‘taiwan_cain’, ‘127.0.0.1’, 3306, config[‘account’], config[‘password’]);
}

if(mysql_taiwan_cain_2nd == null)
{
mysql_taiwan_cain_2nd = api_MYSQL_open(‘taiwan_cain_2nd’, ‘127.0.0.1’, 3306, config[‘account’], config[‘password’]);
}

if(mysql_taiwan_billing == null)
{
mysql_taiwan_billing = api_MYSQL_open(‘taiwan_billing’, ‘127.0.0.1’, 3306, config[‘account’], config[‘password’]);
}

//建库frida
api_MySQL_exec(mysql_taiwan_cain, ‘create database if not exists frida default charset utf8;’);
if(mysql_frida == null)
{
mysql_frida = api_MYSQL_open(‘frida’, ‘127.0.0.1’, 3306, config[‘account’], config[‘password’]);
}

//建表frida.game_event
api_MySQL_exec(mysql_frida, ‘CREATE TABLE game_event (\
event_id varchar(30) NOT NULL, event_info mediumtext NULL,\
PRIMARY KEY  (event_id)\
) ENGINE=InnoDB DEFAULT CHARSET=utf8;’);

//载入活动数据
event_villageattack_load_from_db();
}

//关闭数据库
function uninit_db()
{
//活动数据存档
event_villageattack_save_to_db();

//关闭数据库连接
if(mysql_taiwan_cain)
{
MySQL_close(mysql_taiwan_cain);
mysql_taiwan_cain = null;
}

if(mysql_taiwan_cain_2nd)
{
MySQL_close(mysql_taiwan_cain_2nd);
mysql_taiwan_cain_2nd = null;
}

if(mysql_taiwan_billing)
{
MySQL_close(mysql_taiwan_billing);
mysql_taiwan_billing = null;
}

if(mysql_frida)
{
MySQL_close(mysql_frida);
mysql_frida = null;
}
}

//角色登入登出处理
function hook_user_inout_game_world()
{
//登入(GameWorld::reach_game_world)
Interceptor.attach(ptr(0x86C4E50), {

onEnter: function (args) {
this.user = args[1];
},
onLeave: function (retval) {
//怪物攻城活动更新进度
if(villageAttackEventInfo.state != VILLAGEATTACK_STATE_END)
{
//通知客户端打开活动UI
notify_villageattack_score(this.user);

//公告通知客户端活动进度
event_villageattack_broadcast_diffcult();
}
}
});
}

//开启怪物攻城
const Inter_VillageAttackedStart_dispatch_sig = new NativeFunction(ptr(0x84DF47A), ‘pointer’, [‘pointer’, ‘pointer’, ‘pointer’], {“abi”:”sysv”});
//结束怪物攻城
const village_attacked_CVillageMonsterMgr_OnDestroyVillageMonster = new NativeFunction(ptr(0x086B43D4), ‘pointer’, [‘pointer’, ‘int’], {“abi”:”sysv”});
const GlobalData_s_villageMonsterMgr = ptr(0x941F77C);
//获取角色所在队伍
const CUser_GetParty = new NativeFunction(ptr(0x0865514C), ‘pointer’, [‘pointer’], {“abi”:”sysv”});
//获取队伍中玩家
const CParty_get_user = new NativeFunction(ptr(0x08145764), ‘pointer’, [‘pointer’, ‘int’], {“abi”:”sysv”});
//获取角色扩展数据
const CUser_GetCharacExpandData = new NativeFunction(ptr(0x080DD584), ‘pointer’, [‘pointer’, ‘int’], {“abi”:”sysv”});
//绝望之塔层数
const TOD_Layer_TOD_Layer = new NativeFunction(ptr(0x085FE7B4), ‘pointer’, [‘pointer’, ‘int’], {“abi”:”sysv”});
//设置绝望之塔层数
const TOD_UserState_setEnterLayer = new NativeFunction(ptr(0x086438FC), ‘pointer’, [‘pointer’, ‘pointer’], {“abi”:”sysv”});
//获取角色当前持有金币数量
var CInventory_get_money = new NativeFunction(ptr(0x81347D6), ‘int’, [‘pointer’], {“abi”:”sysv”});
//通知客户端更新角色身上装备
const CUser_SendNotiPacket = new NativeFunction(ptr(0x0867BA5C), ‘int’, [‘pointer’, ‘int’, ‘int’, ‘int’], {“abi”:”sysv”});

//获取UTC时间(秒)
const getCurSec = new NativeFunction(Module.getExportByName(null, ‘time’), ‘int’, [‘pointer’], {“abi”:”sysv”});
function api_CSystemTime_getCurSec()
{
return getCurSec(ptr(0));
}

//设置角色当前绝望之塔层数
function api_TOD_UserState_setEnterLayer(user, layer)
{
var tod_layer = Memory.alloc(100);
TOD_Layer_TOD_Layer(tod_layer, layer);
var expand_data = CUser_GetCharacExpandData(user, 13);
TOD_UserState_setEnterLayer(expand_data, tod_layer);
}

//根据角色id查询角色名
function api_get_charac_name_by_charac_no(charac_no)
{
//从数据库中查询角色名
if(api_MySQL_exec(mysql_taiwan_cain, “select charac_name from charac_info where charac_no=” + charac_no + “;”))
{
if(MySQL_get_n_rows(mysql_taiwan_cain) == 1)
{
if(MySQL_fetch(mysql_taiwan_cain))
{
var charac_name = api_MySQL_get_str(mysql_taiwan_cain, 0);
return charac_name;
}
}
}

return charac_no.toString();
}

//怪物攻城活动当前状态
const VILLAGEATTACK_STATE_P1 = 0;               //一阶段
const VILLAGEATTACK_STATE_P2 = 1;               //二阶段
const VILLAGEATTACK_STATE_P3 = 2;               //三阶段
const VILLAGEATTACK_STATE_END = 3;              //活动已结束

const TAU_CAPTAIN_MONSTER_ID = 50071;           //牛头统帅id(P1阶段击杀该怪物可提升活动难度等级)
const GBL_POPE_MONSTER_ID = 262;                //GBL教主教(P2/P3阶段城镇存在该怪物 持续减少PT点数)
const TAU_META_COW_MONSTER_ID = 17;             //机械牛(P3阶段世界BOSS)

const EVENT_VILLAGEATTACK_START_HOUR = 12;      //每日北京时间20点开启活动
const EVENT_VILLAGEATTACK_TARGET_SCORE = [100, 200, 300];      //各阶段目标PT
const EVENT_VILLAGEATTACK_TOTAL_TIME = 1800;    //活动总时长(秒)

//怪物攻城活动数据
var villageAttackEventInfo = {
‘state’: VILLAGEATTACK_STATE_END,           //活动当前状态
‘score’: 0,                                 //当前阶段频道内总PT
‘start_time’: 0,                            //活动开始时间(UTC)
‘difficult’: 0,                             //活动难度(0-4)
‘next_village_monster_id’: 0,               //下次刷新的攻城怪物id
‘last_killed_monster_id’: 0,                //上次击杀的攻城怪物id
‘p2_last_killed_monster_time’: 0,           //P2阶段上次击杀攻城怪物时间
‘p2_kill_combo’: 0,                         //P2阶段连续击杀相同攻城怪物数量
‘gbl_cnt’: 0,                               //城镇中存活的GBL主教数量
‘defend_success’: 0,                        //怪物攻城活动防守成功

‘user_pt_info’: {},                         //角色个人pt数据
}

//从数据库载入怪物攻城活动数据
function event_villageattack_load_from_db()
{
if(api_MySQL_exec(mysql_frida, “select event_info from game_event where event_id = ‘villageattack’;”))
{
if(MySQL_get_n_rows(mysql_frida) == 1)
{
MySQL_fetch(mysql_frida);

var info = api_MySQL_get_str(mysql_frida, 0);

villageAttackEventInfo = JSON.parse(info);
}
}
}

//怪物攻城活动数据存档
function event_villageattack_save_to_db()
{
api_MySQL_exec(mysql_frida, “replace into game_event (event_id, event_info) values (‘villageattack’, ‘” + JSON.stringify(villageAttackEventInfo) + “‘);”);
}

//世界广播怪物攻城活动当前进度/难度
function event_villageattack_broadcast_diffcult()
{
if(villageAttackEventInfo.state != VILLAGEATTACK_STATE_END)
{
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 当前阶段:’ + (villageAttackEventInfo.state + 1) + ‘, 当前难度等级: ‘ + villageAttackEventInfo.difficult, 14);
}
}

//计算活动剩余时间
function event_villageattack_get_remain_time()
{
var cur_time = api_CSystemTime_getCurSec();
var event_end_time = villageAttackEventInfo.start_time + EVENT_VILLAGEATTACK_TOTAL_TIME;
var remain_time = event_end_time – cur_time;
return remain_time;
}

//怪物攻城活动计时器(每5秒触发一次)
function event_villageattack_timer()
{
if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)
return;

//活动结束检测
var remain_time = event_villageattack_get_remain_time();
if(remain_time <= 0)
{
//活动结束
on_end_event_villageattack();
return;
}

//当前应扣除的PT
var damage = 0;

//P2/P3阶段GBL主教扣PT
if((villageAttackEventInfo.state == VILLAGEATTACK_STATE_P2) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3))
{
for(var i=0; i<villageAttackEventInfo.gbl_cnt; ++i)
{
if(get_random_int(0, 100) < (4 + villageAttackEventInfo.difficult))
{
damage += 1;
}
}
}

//P3阶段世界BOSS自身回血
if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3)
{
if(get_random_int(0, 100) < (6 + villageAttackEventInfo.difficult))
{
damage += 1;
}
}

//扣除PT
if(damage > 0)
{
villageAttackEventInfo.score -= damage;
if(villageAttackEventInfo.score < EVENT_VILLAGEATTACK_TARGET_SCORE[villageAttackEventInfo.state-1])
{
villageAttackEventInfo.score = EVENT_VILLAGEATTACK_TARGET_SCORE[villageAttackEventInfo.state-1]
}

//更新PT
gameworld_update_villageattack_score();
}

//重复触发计时器
if(villageAttackEventInfo.state != VILLAGEATTACK_STATE_END)
{
api_scheduleOnMainThread_delay(event_villageattack_timer, null, 5000);
}
}

//设置怪物攻城副本难度(0-4: 普通-英雄)
function set_villageattack_dungeon_difficult(difficult)
{
Memory.protect(ptr(0x085B9605), 4, ‘rwx’);      //修改内存保护属性为可写
ptr(0x085B9605).writeInt(difficult);
}

//怪物攻城活动相关patch
function hook_VillageAttack()
{
//hook挑战攻城怪物副本结束事件, 更新怪物攻城活动各阶段状态
//village_attacked::CVillageMonster::SendVillageMonsterFightResult
Interceptor.attach(ptr(0x086B330A), {
onEnter: function (args) {

this.village_monster = args[0];             //当前挑战的攻城怪物
this.user = args[1];                        //当前挑战的角色
this.result = args[2].toInt32();            //挑战结果: 1==成功
},
onLeave: function (retval) {
//玩家杀死了攻城怪物
if(this.result == 1)
{
if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)             //攻城活动已结束
return;

//当前杀死的攻城怪物id
var village_monster_id = this.village_monster.add(2).readUShort();

//当前阶段杀死每只攻城怪物PT点数奖励: (1, 2, 4, 8, 16)
var bonus_pt = 2 ** villageAttackEventInfo.difficult;

//玩家所在队伍
var party = CUser_GetParty(this.user);
if(party.isNull())
return;

//更新队伍中的所有玩家PT点数
for(var i=0; i<4; ++i)
{
var user = CParty_get_user(party, i);
if(!user.isNull())
{
//角色当前PT点数(游戏中的原始PT数据记录在village_attack_dungeon表中)
var charac_no = CUserCharacInfo_getCurCharacNo(user).toString();
if(!(charac_no in villageAttackEventInfo.user_pt_info))
villageAttackEventInfo.user_pt_info[charac_no] = [CUser_get_acc_id(user), 0];   //记录角色accid, 方便离线充值

//更新角色当前PT点数
villageAttackEventInfo.user_pt_info[charac_no][1] += bonus_pt;

//击杀世界BOSS, 额外获得PT奖励
if((village_monster_id == TAU_META_COW_MONSTER_ID) && (villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3))
{
villageAttackEventInfo.user_pt_info[charac_no][1] += 1000*(1+villageAttackEventInfo.difficult);
}
}
}

if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P1)              //怪物攻城一阶段
{
//更新频道内总PT
villageAttackEventInfo.score += bonus_pt;

//P1阶段未完成
if(villageAttackEventInfo.score < EVENT_VILLAGEATTACK_TARGET_SCORE[0])
{
//若杀死了牛头统帅, 则攻城难度+1
if(village_monster_id == TAU_CAPTAIN_MONSTER_ID)
{
if(villageAttackEventInfo.difficult < 4)
{
villageAttackEventInfo.difficult += 1;

//怪物攻城副本难度
set_villageattack_dungeon_difficult(villageAttackEventInfo.difficult);

//下次刷新出的攻城怪物为: 牛头统帅
villageAttackEventInfo.next_village_monster_id = TAU_CAPTAIN_MONSTER_ID;

//公告通知客户端活动进度
event_villageattack_broadcast_diffcult();
}
}
}
else
{
//P1阶段已结束, 进入P2
villageAttackEventInfo.state = VILLAGEATTACK_STATE_P2;
villageAttackEventInfo.score = EVENT_VILLAGEATTACK_TARGET_SCORE[0];
villageAttackEventInfo.p2_last_killed_monster_time = 0;
villageAttackEventInfo.last_killed_monster_id = 0;
villageAttackEventInfo.p2_kill_combo = 0;

//公告通知客户端活动进度
event_villageattack_broadcast_diffcult();
}
}
else if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P2)         //怪物攻城二阶段
{
//计算连杀时间
var cur_time = api_CSystemTime_getCurSec();
var diff_time = cur_time – villageAttackEventInfo.p2_last_killed_monster_time;

//1分钟内连续击杀相同攻城怪物
if((diff_time < 60) && (village_monster_id == villageAttackEventInfo.last_killed_monster_id))
{
//连杀点数+1
villageAttackEventInfo.p2_kill_combo += 1;

if(villageAttackEventInfo.p2_kill_combo >= 3)
{
//三连杀增加当前阶段总PT
villageAttackEventInfo.score += 33;

//重新计算连杀
villageAttackEventInfo.last_killed_monster_id = 0;
villageAttackEventInfo.p2_kill_combo = 0;
}
}
else
{
//重新计算连杀
villageAttackEventInfo.last_killed_monster_id = village_monster_id;
villageAttackEventInfo.p2_kill_combo = 1;
}

//保存本次击杀时间
villageAttackEventInfo.p2_last_killed_monster_time = cur_time;

//P2阶段已结束, 进入P3
if(villageAttackEventInfo.score >= EVENT_VILLAGEATTACK_TARGET_SCORE[1])
{
//P2阶段已结束, 进入P3
villageAttackEventInfo.state = VILLAGEATTACK_STATE_P3;
villageAttackEventInfo.score = EVENT_VILLAGEATTACK_TARGET_SCORE[1];
villageAttackEventInfo.next_village_monster_id = TAU_META_COW_MONSTER_ID;

//公告通知客户端活动进度
event_villageattack_broadcast_diffcult();
}
}
else if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3)         //怪物攻城三阶段
{
//击杀世界boss
if(village_monster_id == TAU_META_COW_MONSTER_ID)
{
//更新世界BOSS血量(PT)
villageAttackEventInfo.score += 25;
//继续刷新世界BOSS
villageAttackEventInfo.next_village_monster_id = TAU_META_COW_MONSTER_ID;

//世界广播
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 世界BOSS已被【’ + api_CUserCharacInfo_getCurCharacName(this.user) + ‘】击杀!’, 14);

//P3阶段已结束
if(villageAttackEventInfo.score >= EVENT_VILLAGEATTACK_TARGET_SCORE[2])
{
//怪物攻城活动防守成功, 立即结束活动
villageAttackEventInfo.defend_success = 1;
api_scheduleOnMainThread(on_end_event_villageattack, null);
return;
}
}
}

//世界广播当前活动进度
gameworld_update_villageattack_score();

//通知队伍中的所有玩家更新PT点数
for(var i=0; i<4; ++i)
{
var user = CParty_get_user(party, i);
if(!user.isNull())
{
notify_villageattack_score(user);
}
}

//更新存活GBL主教数量
if(village_monster_id == GBL_POPE_MONSTER_ID)
{
if(villageAttackEventInfo.gbl_cnt > 0)
{
villageAttackEventInfo.gbl_cnt -= 1;
}
}
}
}
});

//hook 刷新攻城怪物函数, 控制下一只刷新的攻城怪物id
//village_attacked::CVillageMonsterArea::GetAttackedMonster
Interceptor.attach(ptr(0x086B3AEA), {

onEnter: function (args) {
},
onLeave: function (retval) {
//返回值为下一次刷新的攻城怪物
if(retval != 0)
{
//下一只刷新的攻城怪物
var next_village_monster = ptr(retval);
var next_village_monster_id = next_village_monster.readUShort();

//当前刷新的怪物为机制怪物
if((next_village_monster_id == TAU_META_COW_MONSTER_ID) || (next_village_monster_id == TAU_CAPTAIN_MONSTER_ID))
{
//替换为随机怪物
next_village_monster.writeUShort(get_random_int(1, 17));
}

//如果需要刷新指定怪物
if(villageAttackEventInfo.next_village_monster_id)
{
if((villageAttackEventInfo.state == VILLAGEATTACK_STATE_P1) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_P2))
{
//P1 P2阶段立即刷新怪物
next_village_monster.writeUShort(villageAttackEventInfo.next_village_monster_id);
villageAttackEventInfo.next_village_monster_id = 0;
}
else if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3)
{
//P3阶段 几率刷新出世界BOSS
if(get_random_int(0, 100) < 44)
{
next_village_monster.writeUShort(villageAttackEventInfo.next_village_monster_id);
villageAttackEventInfo.next_village_monster_id = 0;

//世界广播
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 世界BOSS已刷新, 请勇士们前往挑战!’, 14);
}
}
}

//统计存活GBL主教数量
if(next_village_monster.readUShort() == GBL_POPE_MONSTER_ID)
{
villageAttackEventInfo.gbl_cnt += 1;
}
}
}
});

//当前正在处理挑战的攻城怪物请求
var state_on_fighting = false;
//当前正在被挑战的怪物id
var on_fighting_village_monster_id = 0;

//hook 挑战攻城怪物函数 控制副本刷怪流程
//CParty::OnFightVillageMonster
Interceptor.attach(ptr(0x085B9596), {

onEnter: function (args) {
state_on_fighting = true;
on_fighting_village_monster_id = 0;
},
onLeave: function (retval) {
on_fighting_village_monster_id = 0;
state_on_fighting = false;
}
});

//village_attacked::CVillageMonster::OnFightVillageMonster
Interceptor.attach(ptr(0x086B3240), {

onEnter: function (args) {

if(state_on_fighting)
{
var village_monster = args[0];

//记录当前正在挑战的攻城怪物id
on_fighting_village_monster_id = village_monster.add(2).readU16();
}
},
onLeave: function (retval) {
}
});

//hook 副本刷怪函数 控制副本内怪物的数量和属性
//MapInfo::Add_Mob
var read_f = new NativeFunction(ptr(0x08151612), ‘int’, [‘pointer’, ‘pointer’], {“abi”:”sysv”});
Interceptor.replace(ptr(0x08151612), new NativeCallback(function (map_info, monster) {

//当前刷怪的副本id
//var map_id = map_info.add(4).readUInt();

//怪物攻城副本
//if((map_id >= 40001) && (map_id <= 40095))

if(state_on_fighting)
{
//怪物攻城活动未结束
if(villageAttackEventInfo != VILLAGEATTACK_STATE_END)
{
//正在挑战世界BOSS
if(on_fighting_village_monster_id == TAU_META_COW_MONSTER_ID)
{
//P3阶段
if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_P3)
{
//副本中有几率刷新出世界BOSS, 当前PT点数越高, 活动难度越大, 刷新出世界BOSS概率越大
if(get_random_int(0, 100) < ((villageAttackEventInfo.score-EVENT_VILLAGEATTACK_TARGET_SCORE[1])+(6*villageAttackEventInfo.difficult)))
{
monster.add(0xc).writeUInt(TAU_META_COW_MONSTER_ID);
}
}
}

if(villageAttackEventInfo.difficult == 0)
{
//难度0: 无变化
return read_f(map_info, monster);
}
else if(villageAttackEventInfo.difficult == 1)
{
//难度1: 怪物等级提升至100级
monster.add(16).writeU8(100);
return read_f(map_info, monster);
}
else if(villageAttackEventInfo.difficult == 2)
{
//难度2: 怪物等级提升至110级; 随机刷新紫名怪
monster.add(16).writeU8(110);
//非BOSS怪
if(monster.add(8).readU8() != 3)
{
if(get_random_int(0, 100) < 50)
{
monster.add(8).writeU8(1);     //怪物类型: 0-3
}
}
return read_f(map_info, monster);
}
else if(villageAttackEventInfo.difficult == 3)
{
//难度3: 怪物等级提升至120级; 随机刷新不灭粉名怪; 怪物数量*2
monster.add(16).writeU8(120);
//非BOSS怪
if(monster.add(8).readU8() != 3)
{
if(get_random_int(0, 100) < 75)
{
monster.add(8).writeU8(2);     //怪物类型: 0-3
}
}

//执行原始刷怪流程
read_f(map_info, monster);

//刷新额外的怪物(同一张地图内, 怪物index和怪物uid必须唯一, 这里为怪物分配新的index和uid)

//额外刷新怪物数量
var cnt = 1;
//新的怪物uid偏移
var uid_offset = 1000;
//返回值
var ret = 0;

while(cnt > 0)
{
–cnt;

//新增怪物index
monster.writeUInt(monster.readUInt()+uid_offset);
//新增怪物uid
monster.add(4).writeUInt(monster.add(4).readUInt()+uid_offset);

//为当前地图刷新额外的怪物
ret = read_f(map_info, monster);
}

return ret;
}
else if(villageAttackEventInfo.difficult == 4)
{
//难度4: 怪物等级提升至127级; 随机刷新橙名怪; 怪物数量*4
monster.add(16).writeU8(127);
//非BOSS怪
if(monster.add(8).readU8() != 3)
{
//英雄级副本精英怪类型等于2的怪为橙名怪
monster.add(8).writeU8(get_random_int(1, 3));     //怪物类型: 0-3
}

//执行原始刷怪流程
read_f(map_info, monster);

//刷新额外的怪物(同一张地图内, 怪物index和怪物uid必须唯一, 这里为怪物分配新的index和uid)

//额外刷新怪物数量
var cnt = 3;
//新的怪物uid偏移
var uid_offset = 1000;
//返回值
var ret = 0;

while(cnt > 0)
{
–cnt;

//新增怪物index
monster.writeUInt(monster.readUInt()+uid_offset);
//新增怪物uid
monster.add(4).writeUInt(monster.add(4).readUInt()+uid_offset);

//为当前地图刷新额外的怪物
ret = read_f(map_info, monster);
}

return ret;
}
}
}

//执行原始刷怪流程
return read_f(map_info, monster);
}, ‘int’, [‘pointer’, ‘pointer’]));

//每次通关额外获取当前等级升级所需经验的0%-10%
//village_attacked::CVillageMonsterMgr::OnKillVillageMonster
Interceptor.attach(ptr(0x086B4866), {

onEnter: function (args) {

this.user = args[1];
this.result = args[2].toInt32();
},
onLeave: function (retval) {
if(retval == 0)
{
//挑战成功
if(this.result)
{
//玩家所在队伍
var party = CUser_GetParty(this.user);
//怪物攻城挑战成功, 给队伍中所有成员发送额外通关发经验
for(var i=0; i<4; ++i)
{
var user = CParty_get_user(party, i);
if(!user.isNull())
{
//随机经验奖励
var cur_level = CUserCharacInfo_get_charac_level(user);
var reward_exp = Math.floor(CUserCharacInfo_get_level_up_exp(user, cur_level) * get_random_int(0, 100) / 1000);

//发经验
api_CUser_gain_exp_sp(user, reward_exp);

//通知玩家获取额外奖励
api_CUser_SendNotiPacketMessage(user, ‘怪物攻城挑战成功, 获取额外经验奖励’ + reward_exp, 0);
}
}
}
}
}
});
}

//开启怪物攻城活动
function start_villageattack()
{
var a3 = Memory.alloc(100);
a3.add(10).writeInt(EVENT_VILLAGEATTACK_TOTAL_TIME);                       //活动剩余时间
a3.add(14).writeInt(villageAttackEventInfo.score);                         //当前频道PT点数
a3.add(18).writeInt(EVENT_VILLAGEATTACK_TARGET_SCORE[2]);                  //成功防守所需点数

Inter_VillageAttackedStart_dispatch_sig(ptr(0), ptr(0), a3);
}

//通知玩家怪物攻城进度
function notify_villageattack_score(user)
{
//玩家当前PT点
var charac_no = CUserCharacInfo_getCurCharacNo(user).toString();
var villageattack_pt = 0;
if(charac_no in villageAttackEventInfo.user_pt_info)
villageattack_pt = villageAttackEventInfo.user_pt_info[charac_no][1];

//计算活动剩余时间
var remain_time = event_villageattack_get_remain_time();
if((remain_time <= 0) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END))
return;

//发包通知角色打开怪物攻城UI并更新当前进度
var packet_guard = api_PacketGuard_PacketGuard();
InterfacePacketBuf_put_header(packet_guard, 0, 248);                                                                        //协议: ENUM_NOTIPACKET_STARTED_VILLAGE_ATTACKED
InterfacePacketBuf_put_int(packet_guard, remain_time);                                                                      //活动剩余时间
InterfacePacketBuf_put_int(packet_guard, villageAttackEventInfo.score);                                                     //当前频道PT点数
InterfacePacketBuf_put_int(packet_guard, EVENT_VILLAGEATTACK_TARGET_SCORE[2]);                                              //成功防守所需点数
InterfacePacketBuf_put_int(packet_guard, villageattack_pt);                                                                 //个人PT点数
InterfacePacketBuf_finalize(packet_guard, 1);
CUser_Send(user, packet_guard);
Destroy_PacketGuard_PacketGuard(packet_guard);
}

//更新怪物攻城当前进度(广播给频道内在线玩家)
function gameworld_update_villageattack_score()
{
//计算活动剩余时间
var remain_time = event_villageattack_get_remain_time();
if((remain_time <= 0) || (villageAttackEventInfo.state == VILLAGEATTACK_STATE_END))
return;

var packet_guard = api_PacketGuard_PacketGuard();
InterfacePacketBuf_put_header(packet_guard, 0, 247);                                                                        //协议: ENUM_NOTIPACKET_UPDATE_VILLAGE_ATTACKED
InterfacePacketBuf_put_int(packet_guard, remain_time);                                                                      //活动剩余时间
InterfacePacketBuf_put_int(packet_guard, villageAttackEventInfo.score);                                                     //当前频道PT点数
InterfacePacketBuf_put_int(packet_guard, EVENT_VILLAGEATTACK_TARGET_SCORE[2]);                                              //成功防守所需点数
InterfacePacketBuf_finalize(packet_guard, 1);
GameWorld_send_all(G_GameWorld(), packet_guard);
Destroy_PacketGuard_PacketGuard(packet_guard);
}

//结束怪物攻城活动(立即销毁攻城怪物, 不开启逆袭之谷, 不发送活动奖励)
function end_villageattack()
{
village_attacked_CVillageMonsterMgr_OnDestroyVillageMonster(GlobalData_s_villageMonsterMgr.readPointer(), 2);
}

//重置活动数据
function reset_villageattack_info()
{
villageAttackEventInfo.state = VILLAGEATTACK_STATE_P1;
villageAttackEventInfo.score = 0;
villageAttackEventInfo.difficult = 0;
villageAttackEventInfo.next_village_monster_id = TAU_CAPTAIN_MONSTER_ID;
villageAttackEventInfo.last_killed_monster_id = 0;
villageAttackEventInfo.p2_kill_combo = 0;

villageAttackEventInfo.user_pt_info = {};

set_villageattack_dungeon_difficult(villageAttackEventInfo.difficult);

villageAttackEventInfo.start_time = api_CSystemTime_getCurSec();
}

//开始怪物攻城活动
function on_start_event_villageattack()
{
//重置活动数据
reset_villageattack_info();

//通知全服玩家活动开始 并刷新城镇怪物
start_villageattack();

//开启活动计时器
api_scheduleOnMainThread_delay(event_villageattack_timer, null, 5000);

//公告通知当前活动进度
event_villageattack_broadcast_diffcult();
}

 

//结束怪物攻城活动
function on_end_event_villageattack()
{
if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)
return;

//设置活动状态
villageAttackEventInfo.state = VILLAGEATTACK_STATE_END;

//立即结束怪物攻城活动
end_villageattack();

//防守成功
if(villageAttackEventInfo.defend_success)
{
//频道内在线玩家发奖

//发信奖励: 金币+道具
var reward_gold = 1000000*(1+villageAttackEventInfo.difficult);     //金币
var reward_item_list = [
[7745, 5*(1+villageAttackEventInfo.difficult)],                 //士气冲天
[2600028, 5*(1+villageAttackEventInfo.difficult)],              //天堂痊愈
[42, 5*(1+villageAttackEventInfo.difficult)],                   //复活币
[3314, 1+villageAttackEventInfo.difficult],                     //绝望之塔通关奖章
];
api_gameworld_send_mail(‘<怪物攻城活动>’, ‘恭喜勇士!’, reward_gold, reward_item_list);

//特殊奖励
api_gameworld_foreach(function (user, args){
//设置绝望之塔当前层数为100层
api_TOD_UserState_setEnterLayer(user, 99);

//随机选择一件穿戴中的装备
var inven = CUserCharacInfo_getCurCharacInvenW(user);
var slot = get_random_int(10, 21);          //12件装备slot范围10-21
var equ = CInventory_GetInvenRef(inven, INVENTORY_TYPE_BODY, slot);
if(Inven_Item_getKey(equ))
{
//读取装备强化等级
var upgrade_level = equ.add(6).readU8();
if(upgrade_level < 31)
{
//提升装备的强化/增幅等级
var bonus_level = get_random_int(1, 1 + villageAttackEventInfo.difficult);
upgrade_level += bonus_level;

if(upgrade_level >= 31)
upgrade_level = 31;

//提升强化/增幅等级
equ.add(6).writeU8(upgrade_level);

//通知客户端更新装备
CUser_SendUpdateItemList(user, 1, 3, slot);
}
}
}, null);

//榜一大哥
var rank_first_charac_no = 0;
var rank_first_account_id = 0;
var max_pt = 0;

//论功行赏
for(var charac_no in villageAttackEventInfo.user_pt_info)
{
//发点券
var account_id = villageAttackEventInfo.user_pt_info[charac_no][0];
var pt = villageAttackEventInfo.user_pt_info[charac_no][1];
var reward_cera = pt*10;                                                   //点券奖励 = 个人PT * 10
api_recharge_cash_cera_offline(account_id, ‘GM’, reward_cera);

//找出榜一大哥
if(pt > max_pt)
{
rank_first_charac_no = charac_no;
rank_first_account_id = account_id;
max_pt = pt;
}
}

//频道内公告活动已结束
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 防守成功, 奖励已发送!’, 14);

if(rank_first_charac_no)
{
//个人积分排行榜第一名 额外获得10倍点券奖励
api_recharge_cash_cera_offline(rank_first_account_id, ‘GM’, max_pt*10);

//频道内广播本轮活动排行榜第一名玩家名字
var rank_first_charac_name = api_get_charac_name_by_charac_no(rank_first_charac_no);
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 恭喜勇士 【’ + rank_first_charac_name + ‘】 成为个人积分排行榜第一名(‘ + max_pt + ‘pt)!’, 14);
}
}
else
{
//防守失败
api_gameworld_foreach(function (user, args){

//获取角色背包
var inven = CUserCharacInfo_getCurCharacInvenW(user);

//在线玩家被攻城怪物随机掠夺一件穿戴中的装备
if(get_random_int(0, 100) < 7)
{
//随机删除一件穿戴中的装备
var slot = get_random_int(10, 21);          //12件装备slot范围10-21
var equ = CInventory_GetInvenRef(inven, INVENTORY_TYPE_BODY, slot);

if(Inven_Item_getKey(equ))
{
Inven_Item_reset(equ);

//通知客户端更新装备
CUser_SendNotiPacket(user, 1, 2, 3);
}
}

//在线玩家被攻城怪物随机掠夺1%-10%所持金币
var rate = get_random_int(1, 11);
var cur_gold = CInventory_get_money(inven);
var tax = Math.floor((rate / 100) * cur_gold);
CInventory_use_money(inven, tax, 0, 0);

//通知客户端更新金币数量
CUser_SendUpdateItemList(user, 1, 0, 0);
}, null);

//频道内公告活动已结束
api_GameWorld_SendNotiPacketMessage(‘<怪物攻城活动> 防守失败, 请勇士们再接再厉!’, 14);
}

//释放空间
villageAttackEventInfo.user_pt_info = {};

//存档
event_villageattack_save_to_db();

//开启怪物攻城活动定时器
start_event_villageattack_timer();
}

//开启怪物攻城活动定时器
function start_event_villageattack_timer()
{
//获取当前系统时间
var cur_time = api_CSystemTime_getCurSec();

//计算距离下次开启怪物攻城活动的时间
var delay_time = (3600*EVENT_VILLAGEATTACK_START_HOUR) – (cur_time % (3600*24));
if(delay_time <= 0)
delay_time += 3600*24;

//log(‘距离下次开启<怪物攻城活动>还有:’ + delay_time/3600 + ‘小时’);

//定时开启活动
api_scheduleOnMainThread_delay(on_start_event_villageattack, null, delay_time*1000);
}

//开启怪物攻城活动
function start_event_villageattack()
{
//patch相关函数, 修复活动流程
hook_VillageAttack();

if(villageAttackEventInfo.state == VILLAGEATTACK_STATE_END)
{
//开启怪物攻城活动定时器
start_event_villageattack_timer();
}
}

function start()
{
log(‘++++++++++++++++++++ frida init ++++++++++++++++++++’);

//加载本地配置文件
load_config(‘frida_config.json’);

//挂接消息分发线程 执行需要在主线程运行的代码
hook_TimerDispatcher_dispatch();

//初始化数据库
api_scheduleOnMainThread(init_db, null);

//开启怪物攻城活动
api_scheduleOnMainThread(start_event_villageattack, null);

//登入登出处理
hook_user_inout_game_world();
}

//延迟加载插件
function awake()
{
//Hook check_argv
Interceptor.attach(ptr(0x829EA5A), {

onEnter: function (args) {
},
onLeave: function (retval) {
//等待check_argv函数执行结束 再加载插件
start();
}
});
}

rpc.exports = {
init: function (stage, parameters) {

if(stage == ‘early’)
{
//首次加载插件 等待服务器初始化后再加载
awake();
}
else
{
//热重载:  直接加载
start();
}

},
dispose: function () {

//关闭数据库
uninit_db();

log(‘——————– frida dispose —————–‘);
}
};

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容