aozhiwei 19a2d692ca 1
2020-05-20 17:34:08 +08:00

1560 lines
52 KiB
C++

#include "precompile.h"
#include <random>
#include <a8/mutable_xobject.h>
#include <a8/timer.h>
#include <a8/udplog.h>
#include <a8/collision.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 "roomobstacle.h"
#include "building.h"
#include "loot.h"
#include "roommgr.h"
#include "app.h"
#include "gamelog.h"
#include "typeconvert.h"
const int ROOM_MAX_PLAYER_NUM = 40;
static long long RoomXGetTickCount(void* context)
{
Room* room = (Room*)context;
return room->frame_no;
}
Room::~Room()
{
}
void Room::Init()
{
if (map_meta->i->map_width() < 1) {
abort();
}
if (map_meta->i->map_height() < 1) {
abort();
}
xtimer.Init(RoomXGetTickCount, this, 100, 100);
xtimer_attacher_.xtimer = &xtimer;
CreateSpawnPoints();
CreateLoots();
CreateDropObjs();
InitObstacleDatas();
ShuaAndroid();
}
void Room::UnInit()
{
App::Instance()->perf.alive_count -= alive_count_;
if (game_over_timer) {
a8::Timer::Instance()->DeleteTimer(game_over_timer);
game_over_timer = nullptr;
}
xtimer_attacher_.ClearTimerList();
for (auto& pair : accountid_hash_) {
PlayerMgr::Instance()->RemovePlayerBySocket(pair.second->socket_handle);
}
for (auto& pair : uniid_hash_) {
delete pair.second;
}
uniid_hash_.clear();
for (auto& pair : later_add_hash_) {
delete pair.second;
}
later_add_hash_.clear();
for (auto& pair : removed_robot_hash_) {
delete pair.second;
}
removed_robot_hash_.clear();
grid_service.UnInit();
}
void Room::Update(int delta_time)
{
xtimer.Update();
if (game_over && frame_no - game_over_frameno > SERVER_FRAME_RATE * 20) {
return;
}
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;
}
}
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(int uniid)
{
Entity* entity = GetEntityByUniId(uniid);
return entity && entity->entity_type == ET_Player && entity->entity_subtype == EST_Player ? (Player*)entity : nullptr;
}
Entity* Room::GetEntityByUniId(int 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->born_point = AllocBornPoint(hum);
if (!hum->born_point) {
hum->SetPos(a8::Vec2(DEFAULT_BORN_POINT_X + rand() % 100,
DEFAULT_BORN_POINT_Y + rand() % 200)
);
} else {
hum->SetPos(hum->born_point->RandPoint());
}
hum->attack_dir = hum->GetPos();
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;
last_add_player_tick = a8::XGetTickCount();
++alive_count_;
++App::Instance()->perf.alive_count;
grid_service.AddHuman(hum);
hum->FindLocation();
hum->RefreshView();
MatchTeam(hum);
while (human_hash_.size() > ROOM_MAX_PLAYER_NUM) {
RandRemoveAndroid();
}
NotifyUiUpdate();
}
int 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()
{
if (gas_data.gas_mode != GasInactive) {
return;
}
int robot_min_num = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_min_num", 5);
int robot_max_num = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_max_num", 10);
int refresh_min_time = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_min_time", 5);
int refresh_max_time = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_max_time", 10);
int robot_num = a8::RandEx(robot_min_num, robot_max_num);
int refresh_time = a8::RandEx(refresh_min_time, refresh_max_time);
if (robot_num > 0 && refresh_time > 0) {
CreateAndroid(robot_num);
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * refresh_time,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->ShuaAndroid();
},
&xtimer_attacher_.timer_list_);
NotifyUiUpdate();
}
}
void Room::CreateAndroid(int robot_num)
{
std::vector<MetaData::Robot>* robot_list = MetaMgr::Instance()->GetRobotList();
if (!robot_list || robot_list->empty()) {
return;
}
MetaData::Player* hum_meta = MetaMgr::Instance()->GetPlayer(40002);
assert(hum_meta);
if (!hum_meta) {
abort();
}
if (robot_num <= 0) {
return;
}
int rnd_skin_robot_idx = rand() % robot_num;
for (int i = 0; i < robot_num; ++i) {
if (human_hash_.size() >= ROOM_MAX_PLAYER_NUM) {
return;
}
MetaData::Robot* robot_meta = nullptr;
int try_count = 0;
while (true) {
MetaData::Robot& tmp_robot_meta = (*robot_list)[rand() % robot_list->size()];
if (refreshed_robot_set_.find(tmp_robot_meta.i->id()) ==
refreshed_robot_set_.end()) {
robot_meta = &tmp_robot_meta;
break;
}
++try_count;
if (try_count > 3000) {
return;
}
}
Android* hum = new Android();
hum->name = robot_meta->i->name();
hum->meta = hum_meta;
hum->robot_meta = robot_meta;
hum->entity_uniid = AllocUniid();
hum->born_point = AllocBornPoint(hum);
if (!hum->born_point) {
hum->SetPos(a8::Vec2(DEFAULT_BORN_POINT_X + rand() % 1400,
DEFAULT_BORN_POINT_Y + rand() % 1500)
);
} else {
hum->SetPos(hum->born_point->RandPoint());
}
hum->attack_dir = hum->GetPos();
hum->attack_dir.Normalize();
hum->attack_dir.Rotate(a8::RandAngle());
hum->move_dir = hum->attack_dir;
hum->room = this;
hum->Initialize();
if (rnd_skin_robot_idx == i) {
MetaData::Dress* skin_meta = MetaMgr::Instance()->RandDress();
if (skin_meta) {
#if 0
hum->skin_meta = skin_meta;
hum->SetSkinInfo(skin_meta->i->id());
#endif
}
}
uniid_hash_[hum->entity_uniid] = hum;
moveable_hash_[hum->entity_uniid] = hum;
human_hash_[hum->entity_uniid] = hum;
++alive_count_;
++App::Instance()->perf.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);
}
refreshed_robot_set_.insert(robot_meta->i->id());
}
}
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->GetPos().Distance(target->GetPos()) < 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->GetPos() - a->GetPos()).Norm() < (hum->GetPos() - b->GetPos()).Norm();
});
return !enemys.empty() ? enemys[0] : nullptr;
}
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);
msg.set_room_uuid(a8::XValue(room_uuid).GetString());
}
void Room::ScatterDrop(a8::Vec2 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) {
a8::Vec2 dir = a8::Vec2::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::DropItem(a8::Vec2 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());
{
a8::Vec2 dir = a8::Vec2::UP;
dir.Rotate(a8::RandAngle());
pos = pos + dir * (25 + rand() % 50);
}
CreateLoot(item_id, pos, drop_num, item_lv);
total_count -= drop_num;
}
}
}
RoomObstacle* Room::CreateObstacle(int id, float x, float y)
{
RoomObstacle* entity = InternalCreateObstacle(id, x, y,
[] (Obstacle*)
{
});
assert(entity);
if (entity) {
entity->BroadcastFullState(this);
}
return entity;
}
int Room::CreateLoot(int equip_id, a8::Vec2 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 0;
}
Loot* entity = new Loot();
entity->room = this;
entity->meta = equip_meta;
entity->entity_uniid = AllocUniid();
entity->SetPos(pos);
#if 1
{
if (entity->GetX() >= map_meta->i->map_width()) {
entity->SetX(map_meta->i->map_width() - 1);
}
if (entity->GetX() < 1.0f) {
entity->SetX(1.0f);
}
if (entity->GetY() >= map_meta->i->map_height()) {
entity->SetY(map_meta->i->map_height() - 1);
}
if (entity->GetY() < 1.0f) {
entity->SetY(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(this);
return entity->entity_uniid;
} else {
return 0;
}
}
void Room::CreateBullet(Human* hum, Weapon* weapon,
a8::Vec2 pos, a8::Vec2 dir, float fly_distance, bool is_tank_skin)
{
if (grid_service.CanAdd(pos.x, pos.y)) {
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->SetPos(pos);
bullet->dir = dir;
bullet->born_pos = pos;
bullet->born_dir = dir;
bullet->fly_distance = fly_distance;
bullet->is_tank_skin = is_tank_skin;
bullet->entity_uniid = AllocUniid();
bullet->Initialize();
AddObjectLater(bullet);
grid_service.AddBullet(bullet);
}
}
void Room::RemoveObjectLater(RoomEntity* entity)
{
auto remove_func = [] (const a8::XParams& param)
{
RoomEntity* entity = (RoomEntity*)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:
{
entity->BroadcastDeleteState(entity->room);
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::OnHumanDie(Human* hum)
{
#if 0
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->area) {
pair.second->DecHP(hum->skill_meta->value1,
hum->entity_uniid,
hum->name,
VW_SelfDetonate);
}
}
}
frame_event.AddExplosionEx(hum, 0, hum->pos, 1);
}
#endif
--alive_count_;
--App::Instance()->perf.alive_count;
NotifyUiUpdate();
}
bool Room::OverBorder(const a8::Vec2 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_meta->i->map_width()) {
return true;
}
}
//检查y轴
{
int up_y = pos.y + radius;
if (up_y > map_meta->i->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->real_dead) {
++num;
break;
}
}
}
return num;
}
bool Room::CanJoin(const std::string& accountid, RoomType_e self_room_type)
{
if (gas_data.gas_mode != GasInactive) {
return false;
}
if (accountid_hash_.find(accountid) != accountid_hash_.end()) {
return false;
}
if (self_room_type == RT_NewBrid) {
int remain_time_ms = MetaMgr::Instance()->gas_inactive_time * 1000 -
(frame_no - gas_data.gas_start_frameno) * FRAME_RATE_MS;
remain_time_ms = std::max(remain_time_ms, 0);
if (remain_time_ms <= MetaMgr::Instance()->newbie_wait_time * 1000) {
return false;
}
}
return accountid_hash_.size() < ROOM_MAX_PLAYER_NUM;
}
void Room::OnPlayerOffline(Player* hum)
{
bool has_player = false;
for (auto& pair : accountid_hash_) {
if (pair.second->socket_handle != 0) {
has_player = true;
}
}
if (!has_player) {
RoomMgr::Instance()->AddOverRoom(room_uuid);
}
}
Entity* Room::FindFirstCollisonEntity(const a8::Vec2& aabb_pos, AabbCollider& aabb_box)
{
Entity* target = nullptr;
std::set<GridCell*> grid_list;
grid_service.GetAllCellsByXy(aabb_pos.x, aabb_pos.y, grid_list);
for (auto& grid : grid_list) {
for (Entity* entity : grid->entity_list) {
switch (entity->entity_type) {
case ET_Obstacle:
{
if (!target) {
if (entity->TestCollisionEx(this, aabb_pos, aabb_box)) {
target = entity;
}
}
}
break;
case ET_Building:
{
if (!target || target->entity_type != ET_Building) {
AabbCollider building_aabb_box;
entity->GetAabbBox(building_aabb_box);
if (building_aabb_box.IntersectEx(aabb_pos, &aabb_box)) {
target = entity;
}
}
}
break;
default:
break;
}
}
}
return target;
}
void Room::FindLocationWithAabb(Entity* target, const a8::Vec2& aabb_pos, AabbCollider* aabb_box,
float& new_x, float& new_y)
{
a8::Vec2 old_pos = aabb_pos;
a8::Vec2 new_pos = aabb_pos;
AabbCollider target_collider;
target->GetAabbBox(target_collider);
{
bool ret = aabb_box->CalcSafePointEx(aabb_pos, &target_collider, new_pos);
if (!ret) {
abort();
}
}
a8::Vec2 new_pos_dir = new_pos - old_pos;
new_pos_dir.Normalize();
float distance = (new_pos - old_pos).Norm();
for (int i = distance; i < 100000; i += 5) {
a8::Vec2 pos = old_pos + new_pos_dir * i;
new_x = pos.x;
new_y = pos.y;
bool is_collision = false;
std::set<ColliderComponent*> colliders;
map_service->GetColliders(this, pos.x, pos.y, colliders);
for (ColliderComponent* collider : colliders) {
if (collider->IntersectEx(pos, aabb_box)) {
is_collision = true;
break;
}
}
if (!is_collision) {
break;
}
}
}
void Room::FillSMUiUpdate(cs::SMUiUpdate& msg)
{
for (auto& pair : car_hash_) {
if (!pair.second.taken) {
cs::MFCar* car = msg.add_car_list();
car->set_car_id(pair.second.car_id);
TypeConvert::ToPb(pair.second.pos, car->mutable_pos());
}
}
}
std::set<Human*>* Room::GetAliveTeam()
{
for (auto& pair : team_hash_) {
for (Human* hum : pair.second) {
if (!hum->real_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:
UpdateGasInactive();
break;
case GasJump:
abort();
break;
case GasWaiting:
UpdateGasWaiting();
break;
case GasMoving:
UpdateGasMoving();
break;
}
if (gas_data.gas_mode != GasInactive && gas_data.gas_mode != GasJump) {
if (!game_over && alive_count_ <= MAX_TEAM_NUM && GetAliveTeamNum() <= 1) {
game_over = true;
game_over_frameno = frame_no;
OnGameOver();
}
for (auto& pair : human_hash_) {
if (pair.second->real_dead) {
continue;
}
bool b1 = a8::CircleContainCircle(gas_data.pos_old,
gas_data.gas_progress,
pair.second->GetPos(),
pair.second->GetRadius()
);
bool b2 = a8::CircleContainCircle(gas_data.pos_new,
gas_data.rad_new,
pair.second->GetPos(),
pair.second->GetRadius()
);
if (!b1 && !b2) {
pair.second->poisoning = true;
} else {
pair.second->poisoning = false;
pair.second->poisoning_time = false;
}
}
}
}
void Room::UpdateGasInactive()
{
if (frame_no - gas_data.gas_start_frameno >=
MetaMgr::Instance()->gas_inactive_time * SERVER_FRAME_RATE) {
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 = a8::Vec2(map_meta->i->map_width() / 2.0f,
map_meta->i->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;
if (human_hash_.size() < ROOM_MAX_PLAYER_NUM) {
CreateAndroid(ROOM_MAX_PLAYER_NUM - human_hash_.size());
NotifyUiUpdate();
}
CombineTeam();
NotifyGameStart();
NotifyWxVoip();
InitAirDrop();
}
}
void Room::UpdateGasWaiting()
{
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;;
}
}
void Room::UpdateGasMoving()
{
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 (!(gas_data.pos_new == gas_data.pos_old)) {
a8::Vec2 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 {
a8::Vec2 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) {
assert(gas_data.pos_new == gas_data.pos_old);
}
}
if (std::abs(gas_data.gas_progress - gas_data.rad_new) <= 0.001f) {
int pre_area_id = gas_data.new_area_meta->i->id();
a8::Vec2 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();
}
}
}
bool Room::GenSmallCircle(a8::Vec2 big_circle_pos, float big_circle_rad, float small_circle_rad,
a8::Vec2& out_pos)
{
if (big_circle_rad <= small_circle_rad) {
abort();
}
a8::Vec2 dir = a8::Vec2::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::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);
}
if (pair.second->team_members->size() < MAX_TEAM_NUM) {
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::CombineTeam()
{
std::map<int, size_t> need_combine_teams;
std::map<int, size_t> need_combine_teams_copy;
int first_team_id = 0;
int total_count = 0;
for (auto& pair : team_hash_) {
for (Human* hum : pair.second) {
if (hum->auto_fill && !hum->team_uuid.empty()) {
if (first_team_id == 0) {
first_team_id = pair.first;
}
need_combine_teams[pair.first] = pair.second.size();
total_count += pair.second.size();
break;
}
}
}
if (total_count <= 1) {
return;
}
int first_team_num = 4;
switch (total_count) {
case 3:
{
first_team_num = 2;
break;
}
case 4:
{
first_team_num = 2;
break;
}
case 5:
{
first_team_num = 3;
break;
}
default:
{
first_team_num = 4;
break;
}
}
need_combine_teams_copy = need_combine_teams;
int try_count = 0;
do {
for (auto& pair1 : need_combine_teams) {
bool combine_ok = false;
for (auto& pair2 : need_combine_teams_copy) {
int team_id1 = pair1.first;
int team_id2 = pair2.first;
if (team_id2 == first_team_id) {
continue;
}
if (team_id1 == first_team_id) {
if (pair1.second + pair2.second > first_team_num) {
continue;
}
}
if (team_id1 != team_id2) {
if (pair1.second + pair2.second <= MAX_TEAM_NUM) {
int new_team_num = pair1.second + pair2.second;
{
std::set<Human*>& team1_members = team_hash_[team_id1];
std::set<Human*>& team2_members = team_hash_[team_id2];
if (team1_members.size() + team2_members.size() > MAX_TEAM_NUM) {
a8::UdpLog::Instance()->Warning("team_member > 4 :%d",
{
team1_members.size() + team2_members.size()
});
}
if (pair1.first == first_team_id || pair1.second >= pair2.second) {
for (Human* hum : team2_members) {
hum->team_id = team_id1;
hum->team_members = &team1_members;
team1_members.insert(hum);
}
team_hash_.erase(pair2.first);
} else {
for (Human* hum : team1_members) {
hum->team_id = team_id2;
hum->team_members = &team2_members;
team2_members.insert(hum);
}
team_hash_.erase(pair1.first);
}
}
if (first_team_id == team_id1) {
if (pair1.second + pair2.second >= first_team_num) {
need_combine_teams.erase(team_id1);
need_combine_teams_copy.erase(team_id1);
} else {
pair1.second = new_team_num;
if (need_combine_teams_copy.find(pair1.first) !=
need_combine_teams_copy.end()) {
need_combine_teams_copy[pair1.first] = new_team_num;
}
}
need_combine_teams.erase(team_id2);
need_combine_teams_copy.erase(team_id2);
} else {
if (pair1.second >= pair2.second) {
if (pair1.second + pair2.second >= MAX_TEAM_NUM) {
need_combine_teams.erase(team_id1);
need_combine_teams_copy.erase(team_id1);
} else {
pair1.second = new_team_num;
if (need_combine_teams_copy.find(pair1.first) !=
need_combine_teams_copy.end()) {
need_combine_teams_copy[pair1.first] = new_team_num;
}
}
need_combine_teams.erase(team_id2);
need_combine_teams_copy.erase(team_id2);
} else {
if (pair1.second + pair2.second >= MAX_TEAM_NUM) {
need_combine_teams.erase(team_id2);
need_combine_teams_copy.erase(team_id2);
} else {
pair2.second = new_team_num;
if (need_combine_teams.find(pair2.first) !=
need_combine_teams.end()) {
need_combine_teams[pair2.first] = new_team_num;
}
}
need_combine_teams.erase(team_id1);
need_combine_teams_copy.erase(team_id1);
}
}
combine_ok = true;
break;
}
}
}//end for
if (combine_ok) {
break;
}
}
} while (++try_count < 20);
}
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 && thing_meta->i->type() == 2) {
a8::Vec2 dir = a8::Vec2::UP;
dir.Rotate(a8::RandAngle());
a8::Vec2 box_pos = gas_data.pos_new + dir * (500 + rand() % 300);
if (box_pos.x < 1.0f) {
box_pos.x = 1.0f;
}
if (box_pos.x >= map_meta->i->map_width()) {
box_pos.x = map_meta->i->map_width() - 1;
}
if (box_pos.y < 1.0f) {
box_pos.y = 1.0f;
}
if (box_pos.y >= map_meta->i->map_height()) {
box_pos.y = map_meta->i->map_height() - 1;
}
AabbCollider air_drop_aabb_box;
{
air_drop_aabb_box._min.x = 0 - thing_meta->i->width()/2.0f;
air_drop_aabb_box._min.y = 0 - thing_meta->i->height()/2.0f;
air_drop_aabb_box._max.x = 0 + thing_meta->i->width()/2.0f;
air_drop_aabb_box._max.y = 0 + thing_meta->i->height()/2.0f;
}
Entity* target = FindFirstCollisonEntity(box_pos, air_drop_aabb_box);
if (target) {
float new_x = box_pos.x;
float new_y = box_pos.y;
FindLocationWithAabb(target, box_pos, &air_drop_aabb_box, new_x, new_y);
box_pos.x = new_x;
box_pos.y = new_y;
}
if (box_pos.x + thing_meta->i->width()/2 + 2 > map_meta->i->map_width() ||
box_pos.x - thing_meta->i->width()/2 - 2 < 1 ||
box_pos.y + thing_meta->i->height()/2 + 2 > map_meta->i->map_height() ||
box_pos.y - thing_meta->i->height()/2 - 2 < 1
) {
return;
}
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_);
}
}
RoomObstacle* Room::InternalCreateObstacle(int id, float x, float y,
std::function<void (Obstacle*)> on_precreate)
{
MetaData::MapThing* thing = MetaMgr::Instance()->GetMapThing(id);
if (thing) {
RoomObstacle* entity = new RoomObstacle();
entity->meta = thing;
entity->room = this;
entity->entity_uniid = AllocUniid();
entity->SetPos(a8::Vec2(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(RoomEntity* entity)
{
auto add_func = [] (const a8::XParams& param)
{
RoomEntity* entity = (RoomEntity*)param.sender.GetUserData();
if (entity->entity_type == ET_Bullet) {
MoveableEntity* moveableentity = (MoveableEntity*)entity;
entity->room->moveable_hash_[entity->entity_uniid] = moveableentity;
}
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();
}
}
RoomMgr::Instance()->AddOverRoom(room_uuid);
}
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);
}
if (hum->born_point) {
--hum->born_point->android_num;
}
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_;
--App::Instance()->perf.alive_count;
for (auto& pair : human_hash_) {
pair.second->RemovePartObjects(hum);
}
}
}
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::UpdateCarObject(int old_uniid, int new_uniid, a8::Vec2 pos)
{
auto itr = car_hash_.find(old_uniid);
if (itr != car_hash_.end()) {
CarObject new_obj = itr->second;
new_obj.pos = pos;
car_hash_[new_uniid] = new_obj;
car_hash_.erase(old_uniid);
} else {
assert(false);
}
}
void Room::TakeOnCarObject(int car_uniid)
{
auto itr = car_hash_.find(car_uniid);
if (itr != car_hash_.end()) {
itr->second.taken = true;
}
}
void Room::TakeOffCarObject(int car_uniid, a8::Vec2 pos)
{
auto itr = car_hash_.find(car_uniid);
if (itr != car_hash_.end()) {
itr->second.pos = pos;
itr->second.taken = false;
}
}
int Room::CreateAndTakeonCar(int car_id, a8::Vec2 pos)
{
MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(car_id);
if (!item_meta) {
return -1;
}
int car_uniid = AllocUniid();
CarObject car;
car.car_id = car_id;
car.pos = pos;
car.taken = true;
car_hash_[car_uniid] = car;
return car_uniid;
}
bool Room::HaveMyTeam(const std::string& team_uuid)
{
if (team_uuid.empty()) {
return false;
}
for (auto& pair : accountid_hash_) {
if (pair.second->team_uuid == team_uuid) {
return true;
}
}
return false;
}
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_);
}
BornPoint* Room::AllocBornPoint(Human* hum)
{
BornPoint* born_point = nullptr;
if (hum->born_point) {
std::vector<BornPoint*> point_list;
std::vector<BornPoint*> free_point_list;
BornPoint* pre_point = nullptr;
for (auto& pair : born_point_hash_) {
if (&pair.second != hum->born_point) {
if (pair.second.player_num + pair.second.android_num <
pair.second.thing_tpl->i->param1()) {
point_list.push_back(&pair.second);
free_point_list.push_back(&pair.second);;
}
} else {
pre_point = &pair.second;
}
}
if (pre_point) {
DecBornPointHumanNum(pre_point, hum);
}
if (!free_point_list.empty()) {
born_point = free_point_list[rand() % free_point_list.size()];
} else {
born_point = !point_list.empty() ? point_list[rand() % point_list.size()] : nullptr;
}
} else {
for (auto& pair : born_point_hash_) {
if (pair.second.player_num + pair.second.android_num < pair.second.thing_tpl->i->param1()) {
born_point = &pair.second;
break;
}
}
}
if (born_point) {
IncBornPointHumanNum(born_point, hum);
}
return born_point;
}
void Room::CreateSpawnPoints()
{
if (!spawn_points || spawn_points->empty()) {
abort();
}
for (auto& thing_tpl : *spawn_points) {
BornPoint born_point;
born_point.thing_tpl = thing_tpl;
born_point_hash_[AllocUniid()] = born_point;
}
}
void Room::CreateLoots()
{
if (loots) {
for (auto& thing_tpl : *loots) {
int thing_id = thing_tpl->RandThing();
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(thing_id);
if (equip_meta) {
int entity_uniid = CreateLoot(
equip_meta->i->id(),
a8::Vec2
(
thing_tpl->i->x(),
thing_tpl->i->y()
),
1,
1
);
if (entity_uniid && equip_meta->i->equip_type() == EQUIP_TYPE_CAR) {
Entity* loot_entity = GetEntityByUniId(entity_uniid);
if (loot_entity && loot_entity->entity_type == ET_Loot) {
((Loot*)loot_entity)->bullet_num = equip_meta->i->clip_volume();
((Loot*)loot_entity)->param1 = MetaMgr::Instance()->max_oil;
((Loot*)loot_entity)->param2 = MetaMgr::Instance()->max_oil;
CarObject car;
car.car_id = equip_meta->i->id();
car.pos = loot_entity->GetPos();
car_hash_[loot_entity->entity_uniid] = car;
}
}
}
}
}
}
void Room::CreateDropObjs()
{
if (buildings) {
for (auto& building : *buildings) {
for (auto& obj : building->meta->i->dropobj()) {
CreateLoot(obj.id(),
a8::Vec2(
building->GetX() + obj.x() - building->meta->i->tilewidth() / 2.0,
building->GetY() + obj.y() - building->meta->i->tileheight() / 2.0
),
1,
1
);
}
}
}
}
void Room::IncBornPointHumanNum(BornPoint* point, Human* hum)
{
switch (hum->entity_subtype) {
case EST_Player:
{
++point->player_num;
}
break;
case EST_Android:
{
++point->android_num;
}
break;
default:
{
}
break;
}
}
void Room::DecBornPointHumanNum(BornPoint* point, Human* hum)
{
switch (hum->entity_subtype) {
case EST_Player:
{
--point->player_num;
}
break;
case EST_Android:
{
--point->android_num;
}
break;
default:
{
}
break;
}
}
void Room::SecondRandPoint()
{
for (auto& pair : accountid_hash_) {
Human* hum = pair.second;
hum->born_point = AllocBornPoint(hum);
}
for (auto& pair : team_hash_) {
Human* target = nullptr;
for (Human* hum : pair.second) {
if (!target) {
target = hum;
} else {
if (target->born_point) {
if (hum->born_point) {
DecBornPointHumanNum(hum->born_point, hum);
}
hum->born_point = target->born_point;
if (hum->born_point) {
IncBornPointHumanNum(hum->born_point, hum);
}
}
} //end if
if (!hum->born_point) {
hum->SetX(DEFAULT_BORN_POINT_X + rand() % 100);
hum->SetY(DEFAULT_BORN_POINT_Y + rand() % 200);
} else {
hum->SetPos(hum->born_point->RandPoint());
}
hum->FindLocation();
hum->RefreshView();
grid_service.MoveHuman(hum);
}
}
}
void Room::NotifyGameStart()
{
cs::SMGameStart msg;
for (auto& pair : accountid_hash_) {
pair.second->SendNotifyMsg(msg);
}
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 1,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->SecondRandPoint();
},
&xtimer_attacher_.timer_list_);
waiting_start = true;
xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 2,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.sender.GetUserData();
room->waiting_start = false;
},
&xtimer_attacher_.timer_list_);
}
void Room::InitObstacleDatas()
{
obstacle_datas_.reserve(FIXED_OBJECT_MAXID);
memset(&obstacle_datas_[0], 0, sizeof(ObstacleData) * FIXED_OBJECT_MAXID);
}
ObstacleData* Room::GetPermanentObstacleData(int entity_uniid)
{
if (entity_uniid >= 0 && entity_uniid < (int)obstacle_datas_.size()) {
return &obstacle_datas_[entity_uniid];
} else {
return nullptr;
}
}