2019-05-22 09:54:49 +08:00

1231 lines
46 KiB
C++

#include "precompile.h"
#include <random>
#include <a8/mutable_xobject.h>
#include <a8/timer.h>
#include <a8/udplog.h>
#include "playermgr.h"
#include "player.h"
#include "cs_proto.pb.h"
#include "room.h"
#include "android.h"
#include "metamgr.h"
#include "bullet.h"
#include "collider.h"
#include "obstacle.h"
#include "building.h"
#include "loot.h"
#include "collision.h"
#include "roommgr.h"
#include "app.h"
#include "hero.h"
const int ROOM_MAX_PLAYER_NUM = 50;
#if 0
const int ANDROID_NUM = 0;
#else
const int ANDROID_NUM = 49;
#endif
static long long RoomXGetTickCount(void* context)
{
Room* room = (Room*)context;
return room->frame_no;
}
Room::~Room()
{
}
void Room::Init()
{
xtimer.Init(RoomXGetTickCount, this, 100, 100);
xtimer_attacher.xtimer = &xtimer;
grid_service.Init(MAP_WIDTH, MAP_HEIGHT, MAP_CELL_WIDTH);
CreateThings();
stats_timer_ = a8::Timer::Instance()->AddRepeatTimer(
1000 * 5,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
if (!room->game_over) {
room->OutputDebugLog();
}
}
);
if (App::Instance()->flags.find(1) != App::Instance()->flags.end()) {
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 3,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->AutoMatchTeam();
},
&xtimer_attacher.timer_list_);
}
InitAirDrop();
ShuaAndroid();
}
void Room::UnInit()
{
xtimer_attacher.ClearTimerList();
if (!stats_timer_) {
a8::Timer::Instance()->DeleteTimer(stats_timer_);
stats_timer_ = nullptr;
}
}
void Room::Update(int delta_time)
{
xtimer.Update();
if (game_over && frame_no - game_over_frameno > SERVER_FRAME_RATE * 20) {
return;
}
long long begin_tick = a8::XGetTickCount();
elapsed_time_ += delta_time;
while (elapsed_time_ >= 50) {
if (frame_no % 2 == 0) {
UpdateGas();
}
for (auto& pair : moveable_hash_) {
if (pair.second->entity_type == ET_Player &&
pair.second->updated_times <= 0) {
if (frame_no % 2 == 0) {
pair.second->Update(50);
pair.second->updated_times++;
}
} else {
pair.second->Update(50);
pair.second->updated_times++;
}
}
if (frame_no % 2 == 0) {
for (auto& pair : human_hash_) {
pair.second->SendUpdateMsg();
}
frame_event.Clear();
}
++frame_no;
elapsed_time_ -= 50;
}
long long end_tick = a8::XGetTickCount();
if (end_tick - begin_tick > profile.max_rundelay) {
profile.max_rundelay = end_tick - begin_tick;
}
}
int Room::GetPlayerNum()
{
return accountid_hash_.size();
}
Player* Room::GetPlayerByAccountId(const std::string& accountid)
{
auto itr = accountid_hash_.find(accountid);
return itr != accountid_hash_.end() ? itr->second : nullptr;
}
Player* Room::GetPlayerByUniId(unsigned short uniid)
{
Entity* entity = GetEntityByUniId(uniid);
return entity->entity_type == ET_Player && entity->entity_subtype == EST_Player ? (Player*)entity : nullptr;
}
Entity* Room::GetEntityByUniId(unsigned short uniid)
{
auto itr = uniid_hash_.find(uniid);
return itr != uniid_hash_.end() ? itr->second : nullptr;
}
int Room::AliveCount()
{
return alive_count_;
}
void Room::AddPlayer(Player* hum)
{
assert(gas_data.gas_mode == GasInactive);
{
hum->pos.x = 3000 + rand() % 100;
hum->pos.y = 3000 + rand() % 200;
hum->attack_dir = hum->pos;
hum->attack_dir.Normalize();
hum->attack_dir.Rotate(a8::RandAngle());
hum->move_dir = hum->attack_dir;
}
hum->entity_uniid = AllocUniid();
hum->room = this;
hum->join_frameno = frame_no;
hum->Initialize();
uniid_hash_[hum->entity_uniid] = hum;
moveable_hash_[hum->entity_uniid] = hum;
accountid_hash_[hum->account_id] = hum;
human_hash_[hum->entity_uniid] = hum;
++alive_count_;
grid_service.AddHuman(hum);
hum->FindLocation();
hum->RefreshView();
MatchTeam(hum);
while (human_hash_.size() > ROOM_MAX_PLAYER_NUM) {
RandRemoveAndroid();
}
NotifyUiUpdate();
}
unsigned short Room::AllocUniid()
{
while (GetEntityByUniId(++current_uniid) ||
later_add_hash_.find(current_uniid) != later_add_hash_.end() ||
current_uniid == 0) {}
return current_uniid;
}
void Room::ShuaAndroid()
{
MetaData::Player* hum_meta = MetaMgr::Instance()->GetPlayer(40002);
assert(hum_meta);
if (!hum_meta) {
abort();
}
for (int i = 0; i < ANDROID_NUM; ++i) {
Android* hum = new Android();
hum->name = a8::Format("机器人%d", {i+1});
hum->meta = hum_meta;
hum->entity_uniid = AllocUniid();
{
hum->pos.x = 3000 + rand() % 1400;
hum->pos.y = 3000 + rand() % 1500;
hum->attack_dir = hum->pos;
hum->attack_dir.Normalize();
hum->attack_dir.Rotate(a8::RandAngle());
hum->move_dir = hum->attack_dir;
}
hum->room = this;
hum->Initialize();
uniid_hash_[hum->entity_uniid] = hum;
moveable_hash_[hum->entity_uniid] = hum;
human_hash_[hum->entity_uniid] = hum;
++alive_count_;
grid_service.AddHuman(hum);
hum->FindLocation();
hum->RefreshView();
{
hum->team_id = NewTeam();
hum->team_members = &team_hash_[hum->team_id];
hum->team_members->insert(hum);
}
}
}
Human* Room::FindEnemy(Human* hum)
{
std::vector<Human*> enemys;
enemys.reserve(50);
EntitySubType_e sub_type = EST_Player;
if ((rand() % 10) < 2) {
sub_type = EST_Android;
}
for (auto& cell : hum->grid_list) {
for (Human* target : cell->human_list) {
if (target->entity_subtype == sub_type &&
!target->dead) {
if (hum->pos.Distance(target->pos) < 300.0f) {
if (target->team_id == 0 ||
target->team_id != hum->team_id
) {
enemys.push_back(target);
}
}
}
}
}
std::sort(enemys.begin(), enemys.end(),
[hum] (Human* a, Human *b) -> bool
{
return (hum->pos - a->pos).Norm() < (hum->pos - b->pos).Norm();
});
return !enemys.empty() ? enemys[0] : nullptr;
}
void Room::BuildingBoxBoundCollisionDetection(Entity* sender, std::vector<Entity*>& objects)
{
ColliderComponent* a_collider = sender->GetBoxBound();
for (auto& pair : uniid_hash_) {
if (pair.second->entity_type == ET_Building) {
ColliderComponent* target_collider = pair.second->GetBoxBound();
if (a_collider->Intersect(target_collider)) {
objects.push_back(pair.second);
}
DestoryCollider(target_collider);
}
}
DestoryCollider(a_collider);
}
void Room::FillSMJoinedNotify(Player* self_hum, cs::SMJoinedNotify& msg)
{
msg.set_team_mode(msg.team_mode());
msg.set_player_id(self_hum->entity_uniid);
msg.set_started(false);
for (auto& pair : uniid_hash_) {
if (pair.second->entity_type == ET_Player) {
Human* hum = (Human*)pair.second;
cs::MFPlayerInfo* info = msg.add_player_infos();
info->set_player_id(hum->entity_uniid);
info->set_team_id(hum->team_id);
info->set_name(hum->name);
}
}
}
void Room::ScatterDrop(Vector2D center, int drop_id)
{
MetaData::Drop* drop_meta = MetaMgr::Instance()->GetDrop(drop_id);
if (drop_meta) {
std::vector<std::tuple<int, int, int>> drop_items;
drop_meta->RandItems(drop_items);
for (auto& item : drop_items) {
Vector2D dir = Vector2D::UP;
dir.Rotate(a8::RandAngle());
DropItem(center + dir * (5 + rand() % 50), std::get<0>(item), std::get<1>(item), std::get<1>(item));
}
}
}
void Room::CreateThings()
{
std::string tpl_name = map_meta->RandTemplate();
std::vector<MetaData::MapTplThing>* things = MetaMgr::Instance()->GetMapTplThing(tpl_name);
if (things) {
for (auto& thing_tpl : *things) {
if (thing_tpl.i->weight() >= rand() % 10000) {
int thing_id = thing_tpl.RandThing();
MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(thing_id);
assert(thing_meta);
if (thing_meta) {
if (thing_meta->i->is_house()) {
CreateBuilding(thing_id, thing_tpl.i->x(), thing_tpl.i->y());
} else {
CreateObstacle(thing_id, thing_tpl.i->x(), thing_tpl.i->y());
}
}
}
}
}
}
void Room::FillSMMapInfo(cs::SMMapInfo& map_info)
{
for (auto& pair :uniid_hash_) {
switch (pair.second->entity_type) {
case ET_Obstacle:
{
Obstacle* entity = (Obstacle*)pair.second;
if (!entity->building) {
cs::MFMapObject* p = map_info.add_objects();
p->set_object_id(entity->meta->i->thing_id());
entity->pos.ToPB(p->mutable_pos());
}
}
break;
case ET_Building:
{
Building* entity = (Building*)pair.second;
cs::MFMapObject* p = map_info.add_objects();
p->set_object_id(entity->building_id);
entity->pos.ToPB(p->mutable_pos());
}
break;
default:
{
}
break;
}
}
}
void Room::DropItem(Vector2D pos, int item_id, int item_count, int item_lv)
{
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(item_id);
if (equip_meta && equip_meta->i->group_num() > 0 && item_count > 0) {
int total_count = item_count;
while (total_count > 0) {
int drop_num = std::min(total_count, equip_meta->i->group_num());
{
Vector2D dir = Vector2D::UP;
dir.Rotate(a8::RandAngle());
pos = pos + dir * (25 + rand() % 50);
}
CreateLoot(item_id, pos, drop_num, item_lv);
total_count -= drop_num;
}
}
}
void Room::CreateBuilding(int thing_id, float building_x, float building_y)
{
MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(thing_id);
if (!thing_meta) {
return;
}
MetaData::Building* building_meta = MetaMgr::Instance()->GetBuilding(thing_meta->i->house_id());
if (building_meta) {
Building* building = new Building();
building->room = this;
building->meta = building_meta;
building->building_id = thing_id;
building->entity_uniid = AllocUniid();
building->pos = Vector2D(building_x, building_y);
building->Initialize();
uniid_hash_[building->entity_uniid] = building;
grid_service.AddEntity(building);
for (size_t door_idx = 0; door_idx < building_meta->doors.size(); ++door_idx) {
if (door_idx >= 0 && door_idx < building->meta->doors.size()) {
MetaData::Building::Door* door_meta = &building->meta->doors[door_idx];
float x = building->pos.x + door_meta->state0->x() - building->meta->i->tilewidth() / 2.0;
float y = building->pos.y + door_meta->state0->y() - building->meta->i->tileheight() / 2.0;
InternalCreateObstacle(DOOR_THING_ID, x, y,
[building, door_meta] (Obstacle* entity)
{
entity->building = building;
entity->is_door = true;
entity->door_id = door_meta->door_id;
entity->door_state = DoorStateClose;
entity->building = building;
entity->door_house_uniid = building->entity_uniid;
entity->door_state0 = door_meta->state0;
entity->door_state1 = door_meta->state1;
});
}
}
for (auto& obj : building_meta->i->lootobj()) {
float x = building->pos.x + obj.x() - building->meta->i->tilewidth() / 2.0;
float y = building->pos.y + obj.y() - building->meta->i->tileheight() / 2.0;
if (obj._rand_space() > 0) {
int rnd = rand () % obj._rand_space();
for (auto& pair : obj._things()) {
if (rnd <= pair.value()) {
InternalCreateObstacle(pair.key(), x, y,
[building] (Obstacle* entity)
{
entity->building = building;
});
break;
}
}
}
}
for (auto& obj : building_meta->i->dropobj()) {
CreateLoot(obj.id(),
Vector2D(
building->pos.x + obj.x() - building_meta->i->tilewidth() / 2.0,
building->pos.y + obj.y() - building_meta->i->tileheight() / 2.0
),
1,
1
);
}
}
}
Obstacle* Room::CreateObstacle(int id, float x, float y)
{
Obstacle* entity = InternalCreateObstacle(id, x, y,
[] (Obstacle*)
{
});
assert(entity);
if (entity) {
entity->BroadcastFullState();
}
return entity;
}
Hero* Room::CreateHero(Human* hum)
{
Vector2D pos = hum->pos;
{
Vector2D dir = Vector2D::UP;
dir.Rotate(a8::RandAngle());
pos = pos + dir * (25 + rand() % 50);
}
Hero* hero = new Hero();
hero->room = this;
hero->entity_uniid = AllocUniid();
hero->pos = pos;
hero->move_dir = hum->move_dir;
hero->attack_dir = hum->attack_dir;
hero->master = hum;
hero->skin = hum->skin;
hero->backpack = hum->backpack;
hero->helmet = hum->helmet;
hero->chest = hum->chest;
hero->weapon = *hum->curr_weapon;
hero->energy_shield = hum->energy_shield;
hero->Initialize();
#if 1
uniid_hash_[hero->entity_uniid] = hero;
grid_service.AddEntity(hero);
#endif
hero->BroadcastFullState();
return hero;
}
void Room::CreateLoot(int equip_id, Vector2D pos, int count, int equip_lv)
{
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(equip_id);
if (equip_meta) {
if (equip_meta->i->equip_type() == 2 &&
MetaMgr::Instance()->fighting_mode) {
return;
}
Loot* entity = new Loot();
entity->room = this;
entity->meta = equip_meta;
entity->entity_uniid = AllocUniid();
entity->pos = pos;
#if 1
{
if (entity->pos.x >= MAP_WIDTH) {
entity->pos.x = MAP_WIDTH - 1;
}
if (entity->pos.x < 1.0f) {
entity->pos.x = 1.0f;
}
if (entity->pos.y >= MAP_HEIGHT) {
entity->pos.y = MAP_HEIGHT - 1;
}
if (entity->pos.y < 1.0f) {
entity->pos.y = 1.0f;
}
}
#endif
entity->item_id = equip_id;
entity->count = count;
entity->item_level = equip_lv;
entity->Initialize();
uniid_hash_[entity->entity_uniid] = entity;
grid_service.AddEntity(entity);
entity->BroadcastFullState();
}
}
void Room::CreateBullet(Human* hum, Weapon* weapon,
Vector2D pos, Vector2D dir, float fly_distance)
{
Bullet* bullet = new Bullet();
bullet->player = hum;
bullet->room = this;
bullet->gun_meta = weapon->meta;
bullet->gun_upgrade_meta = weapon->upgrade_meta;
bullet->meta = MetaMgr::Instance()->GetEquip(weapon->meta->i->use_bullet());
bullet->pos = pos;
bullet->dir = dir;
bullet->born_pos = pos;
bullet->born_dir = dir;
bullet->fly_distance = fly_distance;
bullet->entity_uniid = AllocUniid();
bullet->Initialize();
AddObjectLater(bullet);
grid_service.AddBullet(bullet);
}
void Room::RemoveObjectLater(Entity* entity)
{
auto remove_func = [] (const a8::XParams& param)
{
Entity* entity = (Entity*)param.sender.GetUserData();
switch (entity->entity_type) {
case ET_Bullet:
{
entity->room->moveable_hash_.erase(entity->entity_uniid);
entity->room->grid_service.DelBullet((Bullet*)entity);
}
break;
case ET_Loot:
case ET_Hero:
{
entity->BroadcastDeleteState();
entity->room->grid_service.DelEntity(entity);
}
break;
case ET_Player:
{
entity->room->moveable_hash_.erase(entity->entity_uniid);
entity->room->human_hash_.erase(entity->entity_uniid);
}
break;
default:
{
abort();
}
break;
}
entity->room->uniid_hash_.erase(entity->entity_uniid);
delete entity;
};
xtimer.AddDeadLineTimerAndAttach(0,
a8::XParams()
.SetSender(entity),
remove_func,
&entity->xtimer_attacher.timer_list_);
}
void Room::FetchBuilding(Human* hum)
{
for (auto& pair : uniid_hash_) {
if (pair.second->entity_type == ET_Building) {
hum->AddToNewObjects(pair.second);
}
}
}
void Room::OnHumanDie(Human* hum)
{
if (hum->skill_meta && hum->skill_meta->i->type() == ST_SelfDetonate) {
for (auto& pair : human_hash_) {
if (pair.second != hum && (pair.second->team_id == 0 || pair.second->team_id != hum->team_id)) {
float distance = (hum->pos - pair.second->pos).Norm();
if (distance <= hum->skill_meta->i->area()) {
pair.second->DecHP(hum->skill_meta->i->value1(),
hum->entity_uniid,
hum->name,
VW_SelfDetonate);
}
}
}
frame_event.AddExplosionEx(hum, 0, hum->pos, 1);
}
--alive_count_;
NotifyUiUpdate();
}
bool Room::OverBorder(const Vector2D pos, float radius)
{
//检查x轴
{
int left_x = pos.x - radius;
if (left_x < 0.001f) {
return true;
}
int right_x = pos.x + radius;
if (right_x > MAP_WIDTH) {
return true;
}
}
//检查y轴
{
int up_y = pos.y + radius;
if (up_y > MAP_HEIGHT) {
return true;
}
int down_y = pos.y - radius;
if (down_y < 0.001f) {
return true;
}
}
return false;
}
Human* Room::GetWatchWarTarget(Human* hum)
{
if (hum->team_members) {
for (auto& member : *hum->team_members) {
if (member != hum && !member->dead) {
return member;
}
}
}
std::vector<Human*> humans;
for (auto& pair : human_hash_) {
if (pair.first != hum->entity_uniid && !pair.second->dead) {
humans.push_back(pair.second);
}
}
if (!humans.empty()) {
Human* target = humans[rand() % humans.size()];
return target;
}
return nullptr;
}
bool Room::BattleStarted()
{
return battle_start_frameno_ != 0;
}
int Room::GetAliveTeamNum()
{
int num = 0;
for (auto& pair : team_hash_) {
for (Human* hum : pair.second) {
if (!hum->dead) {
++num;
break;
}
}
}
return num;
}
bool Room::CanJoin(const std::string& accountid)
{
if (gas_data.gas_mode != GasInactive) {
return false;
}
return accountid_hash_.size() < ROOM_MAX_PLAYER_NUM;
}
std::set<Human*>* Room::GetAliveTeam()
{
for (auto& pair : team_hash_) {
for (Human* hum : pair.second) {
if (!hum->dead) {
return &pair.second;
}
}
}
return nullptr;
}
int Room::NewTeam()
{
++current_teamid;
team_hash_[current_teamid] = std::set<Human*>();
return current_teamid;
}
void Room::TouchPlayerList(a8::XParams param,
std::function<void (Player*, a8::XParams&)> func)
{
if (!func) {
return;
}
for (auto& pair : accountid_hash_) {
if (pair.second) {
func(pair.second, param);
}
}
}
void Room::TouchHumanList(a8::XParams param,
std::function<bool (Human*, a8::XParams&)> func)
{
if (!func) {
return;
}
for (auto& pair : human_hash_) {
if (pair.second) {
if (!func(pair.second, param)) {
break;
}
}
}
}
void Room::TouchEntityList(a8::XParams param,
std::function<bool (Entity*, a8::XParams&)> func)
{
if (!func) {
return;
}
for (auto& pair : uniid_hash_) {
if (pair.second) {
if (!func(pair.second, param)) {
break;
}
}
}
}
void Room::UpdateGas()
{
switch (gas_data.gas_mode) {
case GasInactive:
{
if (frame_no - gas_data.gas_start_frameno >=
MetaMgr::Instance()->gas_inactive_time * SERVER_FRAME_RATE) {
gas_data.gas_mode = GasJump;
gas_data.gas_start_frameno = frame_no;
ShuaPlane();
RoomMgr::Instance()->RemoveFromInactiveRoomHash(room_uuid);
int auto_jump_interval = MetaMgr::Instance()->GetSysParamAsInt("auto_jump_interval");
auto_jump_timer_ = xtimer.AddRepeatTimerAndAttach(SERVER_FRAME_RATE * auto_jump_interval,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
int auto_jump_min_num = MetaMgr::Instance()->GetSysParamAsInt("auto_jump_min_num");
int auto_jump_max_num = MetaMgr::Instance()->GetSysParamAsInt("auto_jump_max_num");
int jump_num = a8::RandEx(auto_jump_min_num, auto_jump_max_num);
for (int i = 0; i < jump_num; ++i) {
room->TouchHumanList(
a8::XParams()
.SetSender(room),
[] (Human* hum, a8::XParams& param) -> bool
{
if (a8::HasBitFlag(hum->status, HS_Fly) && hum->entity_subtype != EST_Player) {
hum->DoJump();
return false;
}
return true;
});
}
},
&xtimer_attacher.timer_list_);
}
}
break;
case GasJump:
{
Vector2D len_vec = plane.dir *
((frame_no - gas_data.gas_start_frameno)*airline_->i->plane_speed() / SERVER_FRAME_RATE);
plane.curr_pos = plane.start_point + len_vec;
if ((plane.end_point - plane.start_point).Norm() <= len_vec.Norm()) {
TouchHumanList(
a8::XParams(),
[] (Human* hum, a8::XParams& param) -> bool
{
if (a8::HasBitFlag(hum->status, HS_Fly)) {
hum->DoJump();
}
return true;
});
for (auto& pair : accountid_hash_) {
if (App::Instance()->flags.find(4) != App::Instance()->flags.end()) {
xtimer.AddRepeatTimerAndAttach(SERVER_FRAME_RATE * 5,
a8::XParams()
.SetSender(this)
.SetParam1(pair.second->entity_uniid),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
Human* hum = room->GetPlayerByUniId(param.param1);
if (hum) {
std::vector<Human*> humans;
for (auto& pair : room->human_hash_) {
if (pair.first != param.param1.GetInt()) {
humans.push_back(pair.second);
}
}
if (!humans.empty()) {
Human* target = humans[rand() % humans.size()];
hum->FollowTarget(target);
}
}
},
&xtimer_attacher.timer_list_);
}
}
gas_data.gas_mode = GasWaiting;
gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(30001);
gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(30002);
gas_data.gas_progress = gas_data.old_area_meta->i->rad();
gas_data.gas_start_frameno = frame_no;
gas_data.pos_old = Vector2D(MAP_WIDTH / 2.0f,
MAP_HEIGHT / 2.0f);
gas_data.pos_old_bk = gas_data.pos_old;
{
bool gen_ok = GenSmallCircle(gas_data.pos_old,
gas_data.old_area_meta->i->rad(),
gas_data.new_area_meta->i->rad(),
gas_data.pos_new);
assert(gen_ok);
}
gas_data.rad_old = gas_data.old_area_meta->i->rad();
gas_data.rad_new = gas_data.new_area_meta->i->rad();
battle_start_frameno_ = frame_no;
xtimer.DeleteTimer(auto_jump_timer_);
auto_jump_timer_ = nullptr;
NotifyWxVoip();
}
}
break;
case GasWaiting:
{
if (frame_no - gas_data.gas_start_frameno >=
gas_data.old_area_meta->i->wait_time() * SERVER_FRAME_RATE) {
gas_data.gas_mode = GasMoving;
gas_data.gas_start_frameno = frame_no;;
}
}
break;
case GasMoving:
{
if (frame_no - gas_data.gas_start_frameno > 0 && !gas_data.is_last_gas) {
float distance = gas_data.old_area_meta->i->shrink_speed() *
((frame_no - gas_data.gas_start_frameno) * (1.0 / SERVER_FRAME_RATE));
gas_data.gas_progress = std::max(gas_data.rad_old - distance, gas_data.rad_new);
#if 0
{
#else
if (!(gas_data.pos_new == gas_data.pos_old)) {
#endif
Vector2D p1 = gas_data.pos_new - gas_data.pos_old_bk;
gas_data.pre_pos_old = gas_data.pos_old;
if (p1.Norm() - distance <= 0.01f) {
gas_data.pos_old = gas_data.pos_new;
} else {
Vector2D dir = p1;
dir.Normalize();
gas_data.pos_old = gas_data.pos_old_bk + dir * distance;
}
if (gas_data.rad_old - distance <= gas_data.rad_new) {
#if 0
assert(gas_data.pos_new == gas_data.pos_old);
#endif
}
}
if (std::abs(gas_data.gas_progress - gas_data.rad_new) <= 0.001f) {
int pre_area_id = gas_data.new_area_meta->i->id();
Vector2D pre_pos = gas_data.pos_new;
if (!MetaMgr::Instance()->GetSafeArea(pre_area_id + 1)) {
gas_data.is_last_gas = true;
return;
}
gas_data.gas_mode = GasWaiting;
gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id);
gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id + 1);
gas_data.gas_progress = gas_data.old_area_meta->i->rad();
gas_data.gas_start_frameno = frame_no;
gas_data.pos_old = pre_pos;
gas_data.pos_old_bk = gas_data.pos_old;
{
bool gen_ok = GenSmallCircle(gas_data.pos_old,
gas_data.old_area_meta->i->rad(),
gas_data.new_area_meta->i->rad(),
gas_data.pos_new);
assert(gen_ok);
}
gas_data.rad_old = gas_data.old_area_meta->i->rad();
gas_data.rad_new = gas_data.new_area_meta->i->rad();
}
}
}
break;
}
if (gas_data.gas_mode != GasInactive && gas_data.gas_mode != GasJump) {
if (!game_over && alive_count_ <= 1) {
game_over = true;
game_over_frameno = frame_no;
OnGameOver();
}
for (auto& pair : human_hash_) {
if (pair.second->dead) {
continue;
}
bool b1 = CircleContainCircle(gas_data.pos_old,
gas_data.gas_progress,
pair.second->pos,
pair.second->GetRadius()
);
bool b2 = CircleContainCircle(gas_data.pos_new,
gas_data.rad_new,
pair.second->pos,
pair.second->GetRadius()
);
if (!b1 && !b2) {
pair.second->poisoning = true;
} else {
pair.second->poisoning = false;
pair.second->poisoning_time = false;
}
}
}
}
bool Room::GenSmallCircle(Vector2D big_circle_pos, float big_circle_rad, float small_circle_rad,
Vector2D& out_pos)
{
if (big_circle_rad <= small_circle_rad) {
abort();
}
Vector2D dir = Vector2D::UP;
dir.Rotate(a8::RandAngle());
float rad = rand() % (int)(big_circle_rad - small_circle_rad);
if (rad <= 0.001f){
rad = 0.001f;
}
out_pos = big_circle_pos + dir * rad;
return true;
}
void Room::OutputDebugLog()
{
a8::UdpLog::Instance()->Debug("roomid:%d max_rundelay:%d frame_no:%d game_over:%d game_over_frameno:%d "
"elapsed_time:%d alive_count:%d current_uniid:%d "
"state:%d accountid_hash.size:%d moveable_hash_.size:%d "
"uniid_hash.size:%d human_hash.size:%d ",
{
room_uuid,
profile.max_rundelay,
frame_no,
game_over ? 1 : 0,
game_over_frameno,
elapsed_time_,
alive_count_,
current_uniid,
(int)gas_data.gas_mode,
accountid_hash_.size(),
moveable_hash_.size(),
uniid_hash_.size(),
human_hash_.size(),
});
{
int entity_num_arr[ET_MAX] = {0};
a8::MutableXObject* logobj = a8::MutableXObject::NewObject();
for (auto& pair : uniid_hash_) {
if (pair.second->entity_type >= 0 && pair.second->entity_type < ET_MAX) {
++(entity_num_arr[pair.second->entity_type]);
}
}
for (int i = 0; i < ET_MAX; ++i) {
logobj->SetVal(a8::XValue(i).GetString(), entity_num_arr[i]);
}
a8::UdpLog::Instance()->Debug("roomid:%d %s", {room_uuid, logobj->ToJsonStr()});
delete logobj;
}
profile.max_rundelay = 0;
}
void Room::AutoMatchTeam()
{
#if 0
std::vector<Human*> humans;
for (auto& pair : human_hash_) {
if (pair.second->team_uuid.empty()) {
humans.push_back(pair.second);
}
}
std::random_shuffle(humans.begin(), humans.end());
std::set<Human*>* team_members = nullptr;
for (size_t i = 0; i < humans.size(); ++i) {
if (i % 4 == 0) {
++current_teamid;
team_hash_[current_teamid] = std::set<Human*>();
auto itr = team_hash_.find(current_teamid);
team_members = &itr->second;
}
humans[i]->team_id = current_teamid;
humans[i]->team_members = team_members;
humans[i]->need_sync_team_data = true;
humans[i]->need_sync_teammate_data = true;
humans[i]->need_sync_active_player = true;
team_members->insert(humans[i]);
}
#endif
}
void Room::MatchTeam(Human* hum)
{
for (auto& pair : human_hash_) {
if (pair.second != hum) {
if (!hum->team_uuid.empty() && pair.second->team_uuid == hum->team_uuid) {
if (!pair.second->team_members) {
pair.second->team_id = NewTeam();
pair.second->team_members = &team_hash_[pair.second->team_id];
pair.second->team_members->insert(pair.second);
}
pair.second->team_members->insert(hum);
hum->team_id = pair.second->team_id;
hum->team_members = pair.second->team_members;
break;
}
}
}
if (hum->team_id == 0) {
hum->team_id = NewTeam();
hum->team_members = &team_hash_[hum->team_id];
hum->team_members->insert(hum);
}
}
void Room::InitAirDrop()
{
std::list<MetaData::AirDrop>& air_drops = MetaMgr::Instance()->GetAirDrops();
for (auto& air_drop : air_drops) {
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * air_drop.i->time(),
a8::XParams()
.SetSender(this)
.SetParam1(air_drop.i->appear_time())
.SetParam2(air_drop.i->drop_id())
.SetParam3(air_drop.i->id()),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
if (!room->game_over) {
room->AirDrop(param.param1, param.param2);
}
},
&xtimer_attacher.timer_list_);
}
}
void Room::AirDrop(int appear_time, int box_id)
{
MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(box_id);
if (thing_meta) {
Vector2D dir = Vector2D::UP;
dir.Rotate(a8::RandAngle());
Vector2D box_pos = gas_data.pos_new + dir * (500 + rand() % 300);
frame_event.AddAirDrop(appear_time, box_id, box_pos);
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * appear_time / 1000.f,
a8::XParams()
.SetSender(this)
.SetParam1(box_id)
.SetParam2(box_pos.x)
.SetParam3(box_pos.y),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
if (!room->game_over) {
room->CreateObstacle(param.param1.GetInt(),
param.param2.GetDouble(),
param.param3.GetDouble());
}
},
&xtimer_attacher.timer_list_);
}
}
void Room::ShuaPlane()
{
airline_ = MetaMgr::Instance()->RandAirLine();
plane.start_point = Vector2D(airline_->start_point_x, airline_->start_point_y);
plane.end_point = Vector2D(airline_->end_point_x, airline_->end_point_y);
plane.dir = plane.end_point - plane.start_point;
plane.dir.Normalize();
plane.curr_pos = plane.start_point;
for (auto& pair : human_hash_) {
a8::SetBitFlag(pair.second->status, HS_Fly);
pair.second->pos = plane.curr_pos;
pair.second->attack_dir = plane.dir;
pair.second->move_dir = plane.dir;
grid_service.MoveHuman(pair.second);
}
}
Obstacle* Room::InternalCreateObstacle(int id, float x, float y,
std::function<void (Obstacle*)> on_precreate)
{
MetaData::MapThing* thing = MetaMgr::Instance()->GetMapThing(id);
if (thing) {
Obstacle* entity = new Obstacle();
entity->room = this;
entity->meta = thing;
entity->building = nullptr;
entity->entity_uniid = AllocUniid();
entity->pos = Vector2D(x, y);
entity->Initialize();
if (on_precreate) {
on_precreate(entity);
}
uniid_hash_[entity->entity_uniid] = entity;
grid_service.AddEntity(entity);
return entity;
}
return nullptr;
}
void Room::AddObjectLater(Entity* entity)
{
auto add_func = [] (const a8::XParams& param)
{
Entity* entity = (Entity*)param.sender.GetUserData();
if (entity->entity_type == ET_Bullet) {
entity->room->moveable_hash_[entity->entity_uniid] = entity;
}
entity->room->uniid_hash_[entity->entity_uniid] = entity;
entity->room->later_add_hash_.erase(entity->entity_uniid);
};
later_add_hash_[entity->entity_uniid] = entity;
xtimer.AddDeadLineTimerAndAttach(0,
a8::XParams()
.SetSender(entity),
add_func,
&entity->xtimer_attacher.timer_list_);
}
void Room::OnGameOver()
{
for (auto& pair : human_hash_) {
if (game_over && game_over_frameno == frame_no) {
pair.second->SendGameOver();
}
}
}
void Room::RandRemoveAndroid()
{
Human* hum = nullptr;
for (auto& pair : human_hash_) {
if (pair.second->entity_subtype == EST_Android) {
hum = pair.second;
break;
}
}
if (hum) {
if (hum->team_id != 0) {
team_hash_.erase(hum->team_id);
}
for (auto& cell : hum->grid_list) {
for (Human* target : cell->human_list) {
target->AddOutObjects(hum);
}
cell->human_list.erase(hum);
}
moveable_hash_.erase(hum->entity_uniid);
uniid_hash_.erase(hum->entity_uniid);
human_hash_.erase(hum->entity_uniid);
removed_robot_hash_[hum->entity_uniid] = hum;
--alive_count_;
}
}
void Room::NotifyUiUpdate()
{
xtimer.AddDeadLineTimerAndAttach(0,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->TouchPlayerList(a8::XParams(),
[] (Player * hum, a8::XParams & param)
{
hum->SendUIUpdate();
});
},
&xtimer_attacher.timer_list_);
}
void Room::NotifyWxVoip()
{
xtimer.AddDeadLineTimerAndAttach(0,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->TouchPlayerList(a8::XParams(),
[] (Player * hum, a8::XParams & param)
{
hum->SendWxVoip();
});
},
&xtimer_attacher.timer_list_);
}