1097 lines
35 KiB
C++
1097 lines
35 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 "building.h"
|
|
#include "loot.h"
|
|
#include "roommgr.h"
|
|
#include "app.h"
|
|
#include "hero.h"
|
|
#include "gamelog.h"
|
|
|
|
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 * 8);
|
|
map_service.Init(MAP_WIDTH / MAP_GRID_WIDTH, MAP_HEIGHT / MAP_GRID_WIDTH, MAP_GRID_WIDTH);
|
|
|
|
CreateThings();
|
|
if (App::Instance()->HasFlag(1)) {
|
|
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_);
|
|
}
|
|
born_points_ = MetaMgr::Instance()->GetMapBornPoints(map_tpl_name);
|
|
if (!born_points_ || born_points_->size() != ROOM_MAX_PLAYER_NUM) {
|
|
abort();
|
|
}
|
|
ShuaAndroid();
|
|
}
|
|
|
|
void Room::UnInit()
|
|
{
|
|
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();
|
|
map_service.UnInit();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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_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);
|
|
while (human_hash_.size() + 1 > ROOM_MAX_PLAYER_NUM) {
|
|
RandRemoveAndroid();
|
|
}
|
|
hum->entity_uniid = AllocUniid();
|
|
hum->born_point = AllocBornPoint();
|
|
{
|
|
const MetaData::MapTplThing& thing_tpl = (*born_points_)[hum->born_point];
|
|
hum->pos.x = thing_tpl.i->x();
|
|
hum->pos.y = thing_tpl.i->y();
|
|
hum->attack_dir = a8::Vec2::UP;
|
|
hum->attack_dir.Normalize();
|
|
if (!thing_tpl.born_angle.empty()){
|
|
float angle = thing_tpl.born_angle[rand() % thing_tpl.born_angle.size()];
|
|
hum->attack_dir.Rotate(angle / 180.0f);
|
|
} else {
|
|
hum->attack_dir.Rotate(a8::RandAngle());
|
|
}
|
|
hum->move_dir = hum->attack_dir;
|
|
}
|
|
hum->room = this;
|
|
hum->join_frameno = frame_no;
|
|
hum->Initialize();
|
|
born_point_human_hash_[hum->born_point] = hum;
|
|
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);
|
|
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();
|
|
}
|
|
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();
|
|
{
|
|
const MetaData::MapTplThing& thing_tpl = (*born_points_)[hum->born_point];
|
|
hum->pos.x = thing_tpl.i->x();
|
|
hum->pos.y = thing_tpl.i->y();
|
|
hum->attack_dir = a8::Vec2::UP;
|
|
hum->attack_dir.Normalize();
|
|
if (!thing_tpl.born_angle.empty()){
|
|
float angle = thing_tpl.born_angle[rand() % thing_tpl.born_angle.size()];
|
|
hum->attack_dir.Rotate(angle / 180.0f);
|
|
} else {
|
|
hum->attack_dir.Rotate(a8::RandAngle());
|
|
}
|
|
hum->move_dir = hum->attack_dir;
|
|
}
|
|
hum->room = this;
|
|
hum->Initialize();
|
|
born_point_human_hash_[hum->born_point] = hum;
|
|
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);
|
|
}
|
|
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->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::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::CreateThings()
|
|
{
|
|
map_tpl_name = map_meta->RandTemplate();
|
|
std::vector<MetaData::MapTplThing>* things = MetaMgr::Instance()->GetMapTplThing(map_tpl_name);
|
|
if (things) {
|
|
for (auto& thing_tpl : *things) {
|
|
if (thing_tpl.i->is_born_point()) {
|
|
continue;
|
|
}
|
|
if (thing_tpl.i->weight() >= rand() % 10000) {
|
|
int thing_id = thing_tpl.RandThing();
|
|
MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(thing_id);
|
|
#if 0
|
|
assert(thing_meta);
|
|
#endif
|
|
if (thing_meta) {
|
|
#if 1
|
|
CreateObstacle(thing_id, thing_tpl.i->x(), thing_tpl.i->y());
|
|
#else
|
|
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());
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Room::DropItem(a8::Vec2 pos, int item_id, int item_count, int item_lv)
|
|
{
|
|
#if 0
|
|
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;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Room::CreateBuilding(int thing_id, float building_x, float building_y)
|
|
{
|
|
#if 0
|
|
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 = a8::Vec2(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(),
|
|
a8::Vec2(
|
|
building->pos.x + obj.x() - building_meta->i->tilewidth() / 2.0,
|
|
building->pos.y + obj.y() - building_meta->i->tileheight() / 2.0
|
|
),
|
|
1,
|
|
1
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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)
|
|
{
|
|
a8::Vec2 pos = hum->pos;
|
|
{
|
|
a8::Vec2 dir = a8::Vec2::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->GetSkin();
|
|
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, 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;
|
|
}
|
|
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,
|
|
a8::Vec2 pos, a8::Vec2 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;
|
|
#if 1
|
|
bullet->meta = weapon->meta;
|
|
#endif
|
|
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 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_;
|
|
NotifyUiUpdate();
|
|
}
|
|
|
|
void Room::OnHumanRevive(Human* hum)
|
|
{
|
|
{
|
|
const MetaData::MapTplThing& thing_tpl = (*born_points_)[hum->born_point];
|
|
hum->pos.x = thing_tpl.i->x();
|
|
hum->pos.y = thing_tpl.i->y();
|
|
hum->attack_dir = a8::Vec2::UP;
|
|
hum->attack_dir.Normalize();
|
|
if (!thing_tpl.born_angle.empty()){
|
|
float angle = thing_tpl.born_angle[rand() % thing_tpl.born_angle.size()];
|
|
hum->attack_dir.Rotate(angle / 180.0f);
|
|
} else {
|
|
hum->attack_dir.Rotate(a8::RandAngle());
|
|
}
|
|
hum->move_dir = hum->attack_dir;
|
|
}
|
|
++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_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;
|
|
}
|
|
if (accountid_hash_.find(accountid) != accountid_hash_.end()) {
|
|
return false;
|
|
}
|
|
if (App::Instance()->HasFlag(5)) {
|
|
return accountid_hash_.size() < 1;
|
|
} else {
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 = GasStarted;
|
|
gas_data.gas_start_frameno = frame_no;
|
|
if (human_hash_.size() < ROOM_MAX_PLAYER_NUM) {
|
|
CreateAndroid(ROOM_MAX_PLAYER_NUM - human_hash_.size());
|
|
NotifyUiUpdate();
|
|
}
|
|
NotifyWxVoip();
|
|
RoomMgr::Instance()->ActiveRoom(room_uuid);
|
|
battle_start_frameno_ = frame_no;
|
|
battle_report_timer_ = xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * MetaMgr::Instance()->game_duration,
|
|
a8::XParams()
|
|
.SetSender(this),
|
|
[] (const a8::XParams& param)
|
|
{
|
|
Room* room = (Room*)param.sender.GetUserData();
|
|
room->BattleReport();
|
|
},
|
|
&xtimer_attacher.timer_list_);
|
|
}
|
|
}
|
|
break;
|
|
case GasStarted:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
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::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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 = 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(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::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);
|
|
}
|
|
born_point_human_hash_.erase(hum->born_point);
|
|
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_;
|
|
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::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_);
|
|
}
|
|
|
|
void Room::BattleReport()
|
|
{
|
|
battle_report_timer_ = nullptr;
|
|
std::vector<Human*> human_list;
|
|
human_list.reserve(human_hash_.size());
|
|
for (auto& pair : human_hash_) {
|
|
human_list.push_back(pair.second);
|
|
}
|
|
std::sort(human_list.begin(), human_list.end(),
|
|
[] (Human* a, Human* b) -> bool
|
|
{
|
|
if (a->stats.kills > b->stats.kills) {
|
|
return true;
|
|
}
|
|
if (a->stats.dead_times < b->stats.dead_times) {
|
|
return true;
|
|
}
|
|
if (a->stats.damage_amount_out > b->stats.damage_amount_out) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
cs::SMGameOver msg;
|
|
msg.set_room_uuid(a8::XValue(room_uuid).GetString());
|
|
int rank = 1;
|
|
for (Human* hum : human_list) {
|
|
auto p = msg.add_player_stats();
|
|
p->set_rank(rank++);
|
|
p->set_player_id(hum->entity_uniid);
|
|
p->set_player_avatar_url(hum->avatar_url);
|
|
p->set_account_id(hum->account_id);
|
|
p->set_kills(hum->stats.kills);
|
|
p->set_dead_times(hum->stats.dead_times);
|
|
}
|
|
for (Human* hum : human_list) {
|
|
if (hum->entity_subtype == EST_Player) {
|
|
hum->SendNotifyMsg(msg);
|
|
}
|
|
}
|
|
game_over = true;
|
|
game_over_frameno = frame_no;
|
|
RoomMgr::Instance()->AddOverRoom(room_uuid);
|
|
}
|
|
|
|
int Room::AllocBornPoint()
|
|
{
|
|
std::set<int> points;
|
|
for (int i = 0; i < ROOM_MAX_PLAYER_NUM; ++i) {
|
|
points.insert(i);
|
|
}
|
|
for (auto& pair : born_point_human_hash_) {
|
|
points.erase(pair.first);
|
|
}
|
|
if (points.empty()) {
|
|
abort();
|
|
return -1;
|
|
}
|
|
std::vector<int> point_vec;
|
|
for (int point : points) {
|
|
point_vec.push_back(point);
|
|
}
|
|
return point_vec[rand() % point_vec.size()];
|
|
}
|