diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2010-05-29 07:25:38 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2010-05-29 07:25:38 +0000 |
| commit | 72c06a258940696093f255fb1061beb58e1cdd0b (patch) | |
| tree | 36b9a7712eec2d4f07837eab9c38ef1c5af85319 /src/game/server | |
| parent | e56feb597bc743677633432f77513b02907fd169 (diff) | |
| download | zcatch-72c06a258940696093f255fb1061beb58e1cdd0b.tar.gz zcatch-72c06a258940696093f255fb1061beb58e1cdd0b.zip | |
copied refactor to trunk
Diffstat (limited to 'src/game/server')
41 files changed, 3124 insertions, 2966 deletions
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 8ba91a80..839088dd 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1,838 +1,820 @@ #include <new> -#include <engine/e_server_interface.h> -#include <engine/e_config.h> -#include <game/server/gamecontext.hpp> -#include <game/mapitems.hpp> +#include <engine/shared/config.h> +#include <game/server/gamecontext.h> +#include <game/mapitems.h> -#include "character.hpp" -#include "laser.hpp" -#include "projectile.hpp" +#include "character.h" +#include "laser.h" +#include "projectile.h" -struct INPUT_COUNT +//input count +struct CInputCount { - int presses; - int releases; + int m_Presses; + int m_Releases; }; -static INPUT_COUNT count_input(int prev, int cur) +CInputCount CountInput(int Prev, int Cur) { - INPUT_COUNT c = {0,0}; - prev &= INPUT_STATE_MASK; - cur &= INPUT_STATE_MASK; - int i = prev; - while(i != cur) + CInputCount c = {0, 0}; + Prev &= INPUT_STATE_MASK; + Cur &= INPUT_STATE_MASK; + int i = Prev; + + while(i != Cur) { i = (i+1)&INPUT_STATE_MASK; if(i&1) - c.presses++; + c.m_Presses++; else - c.releases++; + c.m_Releases++; } return c; } -MACRO_ALLOC_POOL_ID_IMPL(CHARACTER, MAX_CLIENTS) +MACRO_ALLOC_POOL_ID_IMPL(CCharacter, MAX_CLIENTS) -// player -CHARACTER::CHARACTER() -: ENTITY(NETOBJTYPE_CHARACTER) +// Character, "physical" player's part +CCharacter::CCharacter(CGameWorld *pWorld) +: CEntity(pWorld, NETOBJTYPE_CHARACTER) { - proximity_radius = phys_size; + m_ProximityRadius = g_CharPhysSize; + m_Health = 0; + m_Armor = 0; } -void CHARACTER::reset() +void CCharacter::Reset() { - destroy(); + Destroy(); } -bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team) +bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) { - player_state = PLAYERSTATE_UNKNOWN; - emote_stop = -1; - last_action = -1; - active_weapon = WEAPON_GUN; - last_weapon = WEAPON_HAMMER; - queued_weapon = -1; + m_PlayerState = PLAYERSTATE_UNKNOWN; + m_EmoteStop = -1; + m_LastAction = -1; + m_ActiveWeapon = WEAPON_GUN; + m_LastWeapon = WEAPON_HAMMER; + m_QueuedWeapon = -1; - //clear(); - this->player = player; - this->pos = pos; - this->team = team; + m_pPlayer = pPlayer; + m_Pos = Pos; - core.reset(); - core.world = &game.world.core; - core.pos = pos; - game.world.core.characters[player->client_id] = &core; - - reckoning_tick = 0; - mem_zero(&sendcore, sizeof(sendcore)); - mem_zero(&reckoningcore, sizeof(reckoningcore)); + m_Core.Reset(); + m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision()); + m_Core.m_Pos = m_Pos; + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core; + + m_ReckoningTick = 0; + mem_zero(&m_SendCore, sizeof(m_SendCore)); + mem_zero(&m_ReckoningCore, sizeof(m_ReckoningCore)); - game.world.insert_entity(this); - alive = true; - player->force_balanced = false; + GameServer()->m_World.InsertEntity(this); + m_Alive = true; - game.controller->on_character_spawn(this); + GameServer()->m_pController->OnCharacterSpawn(this); return true; } -void CHARACTER::destroy() +void CCharacter::Destroy() { - game.world.core.characters[player->client_id] = 0; - alive = false; + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + m_Alive = false; } -void CHARACTER::set_weapon(int w) +void CCharacter::SetWeapon(int W) { - if(w == active_weapon) + if(W == m_ActiveWeapon) return; - last_weapon = active_weapon; - queued_weapon = -1; - active_weapon = w; - if(active_weapon < 0 || active_weapon >= NUM_WEAPONS) - active_weapon = 0; + m_LastWeapon = m_ActiveWeapon; + m_QueuedWeapon = -1; + m_ActiveWeapon = W; + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH); - game.create_sound(pos, SOUND_WEAPON_SWITCH); + if(m_ActiveWeapon < 0 || m_ActiveWeapon >= NUM_WEAPONS) + m_ActiveWeapon = 0; } -bool CHARACTER::is_grounded() +bool CCharacter::IsGrounded() { - if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5))) + if(GameServer()->Collision()->CheckPoint(m_Pos.x+g_CharPhysSize/2, m_Pos.y+g_CharPhysSize/2+5)) return true; - if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5))) + if(GameServer()->Collision()->CheckPoint(m_Pos.x-g_CharPhysSize/2, m_Pos.y+g_CharPhysSize/2+5)) return true; return false; } -int CHARACTER::handle_ninja() +void CCharacter::HandleNinja() { - if(active_weapon != WEAPON_NINJA) - return 0; + if(m_ActiveWeapon != WEAPON_NINJA) + return; - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000)) + if ((Server()->Tick() - m_Ninja.m_ActivationTick) > (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) { // time's up, return - weapons[WEAPON_NINJA].got = false; - active_weapon = last_weapon; - if(active_weapon == WEAPON_NINJA) - active_weapon = WEAPON_GUN; - set_weapon(active_weapon); - return 0; + m_aWeapons[WEAPON_NINJA].m_Got = false; + m_ActiveWeapon = m_LastWeapon; + if(m_ActiveWeapon == WEAPON_NINJA) + m_ActiveWeapon = WEAPON_GUN; + + SetWeapon(m_ActiveWeapon); + return; } - // force ninja weapon - set_weapon(WEAPON_NINJA); + // force ninja Weapon + SetWeapon(WEAPON_NINJA); - ninja.currentmovetime--; + m_Ninja.m_CurrentMoveTime--; - if (ninja.currentmovetime == 0) + if (m_Ninja.m_CurrentMoveTime == 0) { - // reset player velocity - core.vel *= 0.2f; - //return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON; + // reset velocity + m_Core.m_Vel *= 0.2f; } - if (ninja.currentmovetime > 0) + if (m_Ninja.m_CurrentMoveTime > 0) { - // Set player velocity - core.vel = ninja.activationdir * data->weapons.ninja.velocity; - vec2 oldpos = pos; - move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f); + // Set velocity + m_Core.m_Vel = m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity; + vec2 OldPos = m_Pos; + GameServer()->Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, vec2(g_CharPhysSize, g_CharPhysSize), 0.f); + // reset velocity so the client doesn't predict stuff - core.vel = vec2(0.0f,0.0f); - if ((ninja.currentmovetime % 2) == 0) - { - //create_smoke(pos); - } + m_Core.m_Vel = vec2(0.f, 0.f); - // check if we hit anything along the way + // check if we Hit anything along the way { - CHARACTER *ents[64]; - vec2 dir = pos - oldpos; - float radius = phys_size * 2.0f; //length(dir * 0.5f); - vec2 center = oldpos + dir * 0.5f; - int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + CCharacter *aEnts[64]; + vec2 Dir = m_Pos - OldPos; + float Radius = g_CharPhysSize * 2.0f; + vec2 Center = OldPos + Dir * 0.5f; + int Num = GameServer()->m_World.FindEntities(Center, Radius, (CEntity**)aEnts, 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + for (int i = 0; i < Num; ++i) { - // Check if entity is a player - if (ents[i] == this) + if (aEnts[i] == this) continue; - // make sure we haven't hit this object before - bool balreadyhit = false; - for (int j = 0; j < numobjectshit; j++) + + // make sure we haven't Hit this object before + bool bAlreadyHit = false; + for (int j = 0; j < m_NumObjectsHit; j++) { - if (hitobjects[j] == ents[i]) - balreadyhit = true; + if (m_apHitObjects[j] == aEnts[i]) + bAlreadyHit = true; } - if (balreadyhit) + if (bAlreadyHit) continue; // check so we are sufficiently close - if (distance(ents[i]->pos, pos) > (phys_size * 2.0f)) + if (distance(aEnts[i]->m_Pos, m_Pos) > (g_CharPhysSize * 2.0f)) continue; - // hit a player, give him damage and stuffs... - game.create_sound(ents[i]->pos, SOUND_NINJA_HIT); + // Hit a player, give him damage and stuffs... + GameServer()->CreateSound(aEnts[i]->m_Pos, SOUND_NINJA_HIT); // set his velocity to fast upward (for now) - if(numobjectshit < 10) - hitobjects[numobjectshit++] = ents[i]; + if(m_NumObjectsHit < 10) + m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; - ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, player->client_id,WEAPON_NINJA); + aEnts[i]->TakeDamage(vec2(0, 10.0f), g_pData->m_Weapons.m_Ninja.m_pBase->m_Damage, m_pPlayer->GetCID(), WEAPON_NINJA); } } - return 0; + + return; } - return 0; + return; } -void CHARACTER::do_weaponswitch() +void CCharacter::DoWeaponSwitch() { - if(reload_timer != 0) // make sure we have reloaded - return; - - if(queued_weapon == -1) // check for a queued weapon + // make sure we can switch + if(m_ReloadTimer != 0 || m_QueuedWeapon == -1 || m_aWeapons[WEAPON_NINJA].m_Got) return; - if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible - return; - - // switch weapon - set_weapon(queued_weapon); + // switch Weapon + SetWeapon(m_QueuedWeapon); } -void CHARACTER::handle_weaponswitch() +void CCharacter::HandleWeaponSwitch() { - int wanted_weapon = active_weapon; - if(queued_weapon != -1) - wanted_weapon = queued_weapon; + int WantedWeapon = m_ActiveWeapon; + if(m_QueuedWeapon != -1) + WantedWeapon = m_QueuedWeapon; - // select weapon - int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses; - int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses; + // select Weapon + int Next = CountInput(m_LatestPrevInput.m_NextWeapon, m_LatestInput.m_NextWeapon).m_Presses; + int Prev = CountInput(m_LatestPrevInput.m_PrevWeapon, m_LatestInput.m_PrevWeapon).m_Presses; - if(next < 128) // make sure we only try sane stuff + if(Next < 128) // make sure we only try sane stuff { - while(next) // next weapon selection + while(Next) // Next Weapon selection { - wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS; - if(weapons[wanted_weapon].got) - next--; + WantedWeapon = (WantedWeapon+1)%NUM_WEAPONS; + if(m_aWeapons[WantedWeapon].m_Got) + Next--; } } - if(prev < 128) // make sure we only try sane stuff + if(Prev < 128) // make sure we only try sane stuff { - while(prev) // prev weapon selection + while(Prev) // Prev Weapon selection { - wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1; - if(weapons[wanted_weapon].got) - prev--; + WantedWeapon = (WantedWeapon-1)<0?NUM_WEAPONS-1:WantedWeapon-1; + if(m_aWeapons[WantedWeapon].m_Got) + Prev--; } } - // direct weapon selection - if(latest_input.wanted_weapon) - wanted_weapon = input.wanted_weapon-1; + // Direct Weapon selection + if(m_LatestInput.m_WantedWeapon) + WantedWeapon = m_Input.m_WantedWeapon-1; // check for insane values - if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got) - queued_weapon = wanted_weapon; + if(WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS && WantedWeapon != m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got) + m_QueuedWeapon = WantedWeapon; - do_weaponswitch(); + DoWeaponSwitch(); } -void CHARACTER::fire_weapon() +void CCharacter::FireWeapon() { - if(reload_timer != 0) + if(m_ReloadTimer != 0) return; - do_weaponswitch(); + DoWeaponSwitch(); + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - bool fullauto = false; - if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE) - fullauto = true; + bool FullAuto = false; + if(m_ActiveWeapon == WEAPON_GRENADE || m_ActiveWeapon == WEAPON_SHOTGUN || m_ActiveWeapon == WEAPON_RIFLE) + FullAuto = true; // check if we gonna fire - bool will_fire = false; - if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true; - if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true; - if(!will_fire) + bool WillFire = false; + if(CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses) + WillFire = true; + + if(FullAuto && (m_LatestInput.m_Fire&1) && m_aWeapons[m_ActiveWeapon].m_Ammo) + WillFire = true; + + if(!WillFire) return; // check for ammo - if(!weapons[active_weapon].ammo) + if(!m_aWeapons[m_ActiveWeapon].m_Ammo) { // 125ms is a magical limit of how fast a human can click - reload_timer = 125 * server_tickspeed() / 1000;; - game.create_sound(pos, SOUND_WEAPON_NOAMMO); + m_ReloadTimer = 125 * Server()->TickSpeed() / 1000; + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_NOAMMO); return; } - vec2 projectile_startpos = pos+direction*phys_size*0.75f; + vec2 ProjStartPos = m_Pos+Direction*g_CharPhysSize*0.75f; - switch(active_weapon) + switch(m_ActiveWeapon) { case WEAPON_HAMMER: { - // reset objects hit - numobjectshit = 0; - game.create_sound(pos, SOUND_HAMMER_FIRE); + // reset objects Hit + m_NumObjectsHit = 0; + GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE); - CHARACTER *ents[64]; - int hits = 0; - int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); + CCharacter *aEnts[64]; + int Hits = 0; + int Num = GameServer()->m_World.FindEntities(ProjStartPos, g_CharPhysSize*0.5f, (CEntity**)aEnts, + 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + for (int i = 0; i < Num; ++i) { - CHARACTER *target = ents[i]; - if (target == this) + CCharacter *Target = aEnts[i]; + + //for race mod or any other mod, which needs hammer hits through the wall remove second condition + if ((Target == this) || GameServer()->Collision()->IntersectLine(ProjStartPos, Target->m_Pos, NULL, NULL)) continue; - - // hit a player, give him damage and stuffs... - vec2 fdir = normalize(ents[i]->pos - pos); // set his velocity to fast upward (for now) - game.create_hammerhit(pos); - ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->client_id, active_weapon); - vec2 dir; - if (length(target->pos - pos) > 0.0f) - dir = normalize(target->pos - pos); + GameServer()->CreateHammerHit(m_Pos); + aEnts[i]->TakeDamage(vec2(0.f, -1.f), g_pData->m_Weapons.m_Hammer.m_pBase->m_Damage, m_pPlayer->GetCID(), m_ActiveWeapon); + + vec2 Dir; + if (length(Target->m_Pos - m_Pos) > 0.0f) + Dir = normalize(Target->m_Pos - m_Pos); else - dir = vec2(0,-1); + Dir = vec2(0.f, -1.f); - target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f; - hits++; + Target->m_Core.m_Vel += normalize(Dir + vec2(0.f, -1.1f)) * 10.0f; + Hits++; } - // if we hit anything, we have to wait for the reload - if(hits) - reload_timer = server_tickspeed()/3; + // if we Hit anything, we have to wait for the reload + if(Hits) + m_ReloadTimer = Server()->TickSpeed()/3; } break; case WEAPON_GUN: { - PROJECTILE *proj = new PROJECTILE(WEAPON_GUN, - player->client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.gun_lifetime), + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_GUN, + m_pPlayer->GetCID(), + ProjStartPos, + Direction, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GunLifetime), 1, 0, 0, -1, WEAPON_GUN); - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(player->client_id); - - game.create_sound(pos, SOUND_GUN_FIRE); + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(1); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); + + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + + GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE); } break; case WEAPON_SHOTGUN: { - int shotspread = 2; + int ShotSpread = 2; - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(shotspread*2+1); + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(ShotSpread*2+1); - for(int i = -shotspread; i <= shotspread; i++) + for(int i = -ShotSpread; i <= ShotSpread; ++i) { - float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; - float a = get_angle(direction); - a += spreading[i+2]; - float v = 1-(abs(i)/(float)shotspread); - float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v); - PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN, - player->client_id, - projectile_startpos, - vec2(cosf(a), sinf(a))*speed, - (int)(server_tickspeed()*tuning.shotgun_lifetime), + float Spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; + float a = GetAngle(Direction); + a += Spreading[i+2]; + float v = 1-(absolute(i)/(float)ShotSpread); + float Speed = mix((float)GameServer()->Tuning()->m_ShotgunSpeeddiff, 1.0f, v); + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_SHOTGUN, + m_pPlayer->GetCID(), + ProjStartPos, + vec2(cosf(a), sinf(a))*Speed, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_ShotgunLifetime), 1, 0, 0, -1, WEAPON_SHOTGUN); - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); } - msg_pack_end(); - server_send_msg(player->client_id); + Server()->SendMsg(&Msg, 0,m_pPlayer->GetCID()); - game.create_sound(pos, SOUND_SHOTGUN_FIRE); + GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE); } break; case WEAPON_GRENADE: { - PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE, - player->client_id, - projectile_startpos, - direction, - (int)(server_tickspeed()*tuning.grenade_lifetime), - 1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); - - // pack the projectile and send it to the client directly - NETOBJ_PROJECTILE p; - proj->fill_info(&p); + CProjectile *Proj = new CProjectile(GameWorld(), WEAPON_GRENADE, + m_pPlayer->GetCID(), + ProjStartPos, + Direction, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GrenadeLifetime), + 1, true, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE); + + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + Proj->FillInfo(&p); + + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(1); + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); - msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0); - msg_pack_int(1); - for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) - msg_pack_int(((int *)&p)[i]); - msg_pack_end(); - server_send_msg(player->client_id); - - game.create_sound(pos, SOUND_GRENADE_FIRE); + GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE); } break; case WEAPON_RIFLE: { - new LASER(pos, direction, tuning.laser_reach, player->client_id); - game.create_sound(pos, SOUND_RIFLE_FIRE); + new CLaser(GameWorld(), m_Pos, Direction, GameServer()->Tuning()->m_LaserReach, m_pPlayer->GetCID()); + GameServer()->CreateSound(m_Pos, SOUND_RIFLE_FIRE); } break; case WEAPON_NINJA: { - attack_tick = server_tick(); - ninja.activationdir = direction; - ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000; - - //reload_timer = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick(); + // reset Hit objects + m_NumObjectsHit = 0; - // reset hit objects - numobjectshit = 0; + m_AttackTick = Server()->Tick(); + m_Ninja.m_ActivationDir = Direction; + m_Ninja.m_CurrentMoveTime = g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / 1000; - game.create_sound(pos, SOUND_NINJA_FIRE); - + GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE); } break; } - - if(weapons[active_weapon].ammo > 0) // -1 == unlimited - weapons[active_weapon].ammo--; - attack_tick = server_tick(); - if(!reload_timer) - reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000; + + m_AttackTick = Server()->Tick(); + + if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited + m_aWeapons[m_ActiveWeapon].m_Ammo--; + + if(!m_ReloadTimer) + m_ReloadTimer = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Firedelay * Server()->TickSpeed() / 1000; } -int CHARACTER::handle_weapons() +void CCharacter::HandleWeapons() { - vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y)); - - /* - if(config.dbg_stress) - { - for(int i = 0; i < NUM_WEAPONS; i++) - { - weapons[i].got = true; - weapons[i].ammo = 10; - } - - if(reload_timer) // twice as fast reload - reload_timer--; - } */ - - //if(active_weapon == WEAPON_NINJA) - handle_ninja(); - + //ninja + HandleNinja(); + + vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); // check reload timer - if(reload_timer) + if(m_ReloadTimer) { - reload_timer--; - return 0; + m_ReloadTimer--; + return; } - - /* - if (active_weapon == WEAPON_NINJA) - { - // don't update other weapons while ninja is active - return handle_ninja(); - }*/ - // fire weapon, if wanted - fire_weapon(); + // fire Weapon, if wanted + FireWeapon(); // ammo regen - int ammoregentime = data->weapons.id[active_weapon].ammoregentime; - if(ammoregentime) + int AmmoRegenTime = g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Ammoregentime; + if(AmmoRegenTime) { // If equipped and not active, regen ammo? - if (reload_timer <= 0) + if (m_ReloadTimer <= 0) { - if (weapons[active_weapon].ammoregenstart < 0) - weapons[active_weapon].ammoregenstart = server_tick(); + if (m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart < 0) + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = Server()->Tick(); - if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000) + if ((Server()->Tick() - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart) >= AmmoRegenTime * Server()->TickSpeed() / 1000) { // Add some ammo - weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10); - weapons[active_weapon].ammoregenstart = -1; + m_aWeapons[m_ActiveWeapon].m_Ammo = min(m_aWeapons[m_ActiveWeapon].m_Ammo + 1, 10); + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } else { - weapons[active_weapon].ammoregenstart = -1; + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } - return 0; + return; +} + +bool CCharacter::GiveWeapon(int Weapon, int Ammo) +{ + if(m_aWeapons[Weapon].m_Ammo < g_pData->m_Weapons.m_aId[Weapon].m_Maxammo || !m_aWeapons[Weapon].m_Got) + { + m_aWeapons[Weapon].m_Got = true; + m_aWeapons[Weapon].m_Ammo = min(g_pData->m_Weapons.m_aId[Weapon].m_Maxammo, Ammo); + return true; + } + return false; +} + +void CCharacter::GiveNinja() +{ + m_Ninja.m_ActivationTick = Server()->Tick(); + m_aWeapons[WEAPON_NINJA].m_Got = true; + m_aWeapons[WEAPON_NINJA].m_Ammo = -1; + m_LastWeapon = m_ActiveWeapon; + m_ActiveWeapon = WEAPON_NINJA; + + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA); } -void CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +void CCharacter::SetEmote(int Emote, int Tick) +{ + m_EmoteType = Emote; + m_EmoteStop = Tick; +} + +void CCharacter::OnPredictedInput(CNetObj_PlayerInput *pNewInput) { // check for changes - if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0) - last_action = server_tick(); + if(mem_comp(&m_Input, pNewInput, sizeof(CNetObj_PlayerInput)) != 0) + m_LastAction = Server()->Tick(); // copy new input - mem_copy(&input, new_input, sizeof(input)); - num_inputs++; + mem_copy(&m_Input, pNewInput, sizeof(m_Input)); + m_NumInputs++; // or are not allowed to aim in the center - if(input.target_x == 0 && input.target_y == 0) - input.target_y = -1; + if(m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) + m_Input.m_TargetY = -1; } -void CHARACTER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) +void CCharacter::OnDirectInput(CNetObj_PlayerInput *pNewInput) { - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); - mem_copy(&latest_input, new_input, sizeof(latest_input)); + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); + mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput)); - if(num_inputs > 2 && team != -1) + if(m_NumInputs > 2 && m_pPlayer->GetTeam() != -1) { - handle_weaponswitch(); - fire_weapon(); + HandleWeaponSwitch(); + FireWeapon(); } - mem_copy(&latest_previnput, &latest_input, sizeof(latest_input)); + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); } -void CHARACTER::tick() +void CCharacter::Tick() { - if(player->force_balanced) + if(m_pPlayer->m_ForceBalanced) { - char buf[128]; - str_format(buf, sizeof(buf), "You were moved to %s due to team balancing", game.controller->get_team_name(team)); - game.send_broadcast(buf, player->client_id); + char Buf[128]; + str_format(Buf, sizeof(Buf), "You were moved to %s due to team balancing", GameServer()->m_pController->GetTeamName(m_pPlayer->GetTeam())); + GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID()); - player->force_balanced = false; + m_pPlayer->m_ForceBalanced = false; } - //player_core core; - //core.pos = pos; - //core.jumped = jumped; - core.input = input; - core.tick(true); + m_Core.m_Input = m_Input; + m_Core.Tick(true); - float phys_size = 28.0f; // handle death-tiles - if(col_get((int)(pos.x+phys_size/3), (int)(pos.y-phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x+phys_size/3), (int)(pos.y+phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x-phys_size/3), (int)(pos.y-phys_size/3))&COLFLAG_DEATH || - col_get((int)(pos.x-phys_size/3), (int)(pos.y+phys_size/3))&COLFLAG_DEATH) + if(GameServer()->Collision()->GetCollisionAt(m_Pos.x+g_CharPhysSize/3.f, m_Pos.y-g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x+g_CharPhysSize/3.f, m_Pos.y+g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x-g_CharPhysSize/3.f, m_Pos.y-g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH || + GameServer()->Collision()->GetCollisionAt(m_Pos.x-g_CharPhysSize/3.f, m_Pos.y+g_CharPhysSize/3.f)&CCollision::COLFLAG_DEATH) { - die(player->client_id, WEAPON_WORLD); + Die(m_pPlayer->GetCID(), WEAPON_WORLD); } - // handle weapons - handle_weapons(); + // handle Weapons + HandleWeapons(); - player_state = input.player_state; + m_PlayerState = m_Input.m_PlayerState; // Previnput - previnput = input; + m_PrevInput = m_Input; return; } -void CHARACTER::tick_defered() +void CCharacter::TickDefered() { // advance the dummy { - WORLD_CORE tempworld; - reckoningcore.world = &tempworld; - reckoningcore.tick(false); - reckoningcore.move(); - reckoningcore.quantize(); + CWorldCore TempWorld; + m_ReckoningCore.Init(&TempWorld, GameServer()->Collision()); + m_ReckoningCore.Tick(false); + m_ReckoningCore.Move(); + m_ReckoningCore.Quantize(); } - //lastsentcore; - /*if(!dead) - {*/ - vec2 start_pos = core.pos; - vec2 start_vel = core.vel; - bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f)); - - core.move(); - bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f)); - core.quantize(); - bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f)); - pos = core.pos; - - if(!stuck_before && (stuck_after_move || stuck_after_quant)) - { - dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", - stuck_before, - stuck_after_move, - stuck_after_quant, - start_pos.x, start_pos.y, - start_vel.x, start_vel.y, - *((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y), - *((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y)); - } + //lastsentcore + vec2 StartPos = m_Core.m_Pos; + vec2 StartVel = m_Core.m_Vel; + bool StuckBefore = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + + m_Core.Move(); + bool StuckAfterMove = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Core.Quantize(); + bool StuckAfterQuant = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Pos = m_Core.m_Pos; + + if(!StuckBefore && (StuckAfterMove || StuckAfterQuant)) + { + dbg_msg("char_core", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", + StuckBefore, + StuckAfterMove, + StuckAfterQuant, + StartPos.x, StartPos.y, + StartVel.x, StartVel.y, + *((unsigned *)&StartPos.x), *((unsigned *)&StartPos.y), + *((unsigned *)&StartVel.x), *((unsigned *)&StartVel.y)); + } - int events = core.triggered_events; - int mask = cmask_all_except_one(player->client_id); - - if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask); - - //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); - if(events&COREEVENT_HOOK_ATTACH_PLAYER) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all()); - if(events&COREEVENT_HOOK_ATTACH_GROUND) game.create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask); - if(events&COREEVENT_HOOK_HIT_NOHOOK) game.create_sound(pos, SOUND_HOOK_NOATTACH, mask); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - //} - - if(team == -1) + int Events = m_Core.m_TriggeredEvents; + int Mask = CmaskAllExceptOne(m_pPlayer->GetCID()); + + if(Events&COREEVENT_GROUND_JUMP) GameServer()->CreateSound(m_Pos, SOUND_PLAYER_JUMP, Mask); + + if(Events&COREEVENT_HOOK_ATTACH_PLAYER) GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_PLAYER, CmaskAll()); + if(Events&COREEVENT_HOOK_ATTACH_GROUND) GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_GROUND, Mask); + if(Events&COREEVENT_HOOK_HIT_NOHOOK) GameServer()->CreateSound(m_Pos, SOUND_HOOK_NOATTACH, Mask); + + + if(m_pPlayer->GetTeam() == -1) { - pos.x = input.target_x; - pos.y = input.target_y; + m_Pos.x = m_Input.m_TargetX; + m_Pos.y = m_Input.m_TargetY; } - // update the sendcore if needed + // update the m_SendCore if needed { - NETOBJ_CHARACTER predicted; - NETOBJ_CHARACTER current; - mem_zero(&predicted, sizeof(predicted)); - mem_zero(¤t, sizeof(current)); - reckoningcore.write(&predicted); - core.write(¤t); + CNetObj_Character Predicted; + CNetObj_Character Current; + mem_zero(&Predicted, sizeof(Predicted)); + mem_zero(&Current, sizeof(Current)); + m_ReckoningCore.Write(&Predicted); + m_Core.Write(&Current); // only allow dead reackoning for a top of 3 seconds - if(reckoning_tick+server_tickspeed()*3 < server_tick() || mem_comp(&predicted, ¤t, sizeof(NETOBJ_CHARACTER)) != 0) + if(m_ReckoningTick+Server()->TickSpeed()*3 < Server()->Tick() || mem_comp(&Predicted, &Current, sizeof(CNetObj_Character)) != 0) { - reckoning_tick = server_tick(); - sendcore = core; - reckoningcore = core; + m_ReckoningTick = Server()->Tick(); + m_SendCore = m_Core; + m_ReckoningCore = m_Core; } } } -bool CHARACTER::increase_health(int amount) +bool CCharacter::IncreaseHealth(int Amount) { - if(health >= 10) + if(m_Health >= 10) return false; - health = clamp(health+amount, 0, 10); + m_Health = clamp(m_Health+Amount, 0, 10); return true; } -bool CHARACTER::increase_armor(int amount) +bool CCharacter::IncreaseArmor(int Amount) { - if(armor >= 10) + if(m_Armor >= 10) return false; - armor = clamp(armor+amount, 0, 10); + m_Armor = clamp(m_Armor+Amount, 0, 10); return true; } -void CHARACTER::die(int killer, int weapon) +void CCharacter::Die(int Killer, int Weapon) { - /*if (dead || team == -1) - return;*/ - int mode_special = game.controller->on_character_death(this, game.players[killer], weapon); + int ModeSpecial = GameServer()->m_pController->OnCharacterDeath(this, GameServer()->m_apPlayers[Killer], Weapon); dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", - killer, server_clientname(killer), - player->client_id, server_clientname(player->client_id), weapon, mode_special); + Killer, Server()->ClientName(Killer), + m_pPlayer->GetCID(), Server()->ClientName(m_pPlayer->GetCID()), Weapon, ModeSpecial); // send the kill message - NETMSG_SV_KILLMSG msg; - msg.killer = killer; - msg.victim = player->client_id; - msg.weapon = weapon; - msg.mode_special = mode_special; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_KillMsg Msg; + Msg.m_Killer = Killer; + Msg.m_Victim = m_pPlayer->GetCID(); + Msg.m_Weapon = Weapon; + Msg.m_ModeSpecial = ModeSpecial; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); // a nice sound - game.create_sound(pos, SOUND_PLAYER_DIE); - - // set dead state - // TODO: do stuff here - /* - die_pos = pos; - dead = true; - */ + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE); // this is for auto respawn after 3 secs - player->die_tick = server_tick(); + m_pPlayer->m_DieTick = Server()->Tick(); - alive = false; - game.world.remove_entity(this); - game.world.core.characters[player->client_id] = 0; - game.create_death(pos, player->client_id); + m_Alive = false; + GameServer()->m_World.RemoveEntity(this); + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID()); // we got to wait 0.5 secs before respawning - player->respawn_tick = server_tick()+server_tickspeed()/2; + m_pPlayer->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; } -bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon) +bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) { - core.vel += force; + m_Core.m_Vel += Force; - if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage) + if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), From) && !g_Config.m_SvTeamdamage) return false; - // player only inflicts half damage on self - if(from == player->client_id) - dmg = max(1, dmg/2); + // m_pPlayer only inflicts half damage on self + if(From == m_pPlayer->GetCID()) + Dmg = max(1, Dmg/2); - damage_taken++; + m_DamageTaken++; // create healthmod indicator - if(server_tick() < damage_taken_tick+25) + if(Server()->Tick() < m_DamageTakenTick+25) { // make sure that the damage indicators doesn't group together - game.create_damageind(pos, damage_taken*0.25f, dmg); + GameServer()->CreateDamageInd(m_Pos, m_DamageTaken*0.25f, Dmg); } else { - damage_taken = 0; - game.create_damageind(pos, 0, dmg); + m_DamageTaken = 0; + GameServer()->CreateDamageInd(m_Pos, 0, Dmg); } - if(dmg) + if(Dmg) { - if(armor) + if(m_Armor) { - if(dmg > 1) + if(Dmg > 1) { - health--; - dmg--; + m_Health--; + Dmg--; } - if(dmg > armor) + if(Dmg > m_Armor) { - dmg -= armor; - armor = 0; + Dmg -= m_Armor; + m_Armor = 0; } else { - armor -= dmg; - dmg = 0; + m_Armor -= Dmg; + Dmg = 0; } } - health -= dmg; + m_Health -= Dmg; } - damage_taken_tick = server_tick(); + m_DamageTakenTick = Server()->Tick(); - // do damage hit sound - if(from >= 0 && from != player->client_id && game.players[from]) - game.create_sound(game.players[from]->view_pos, SOUND_HIT, cmask_one(from)); + // do damage Hit sound + if(From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, SOUND_HIT, CmaskOne(From)); // check for death - if(health <= 0) + if(m_Health <= 0) { - die(from, weapon); + Die(From, Weapon); // set attacker's face to happy (taunt!) - if (from >= 0 && from != player->client_id && game.players[from]) + if (From >= 0 && From != m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) { - CHARACTER *chr = game.players[from]->get_character(); - if (chr) + CCharacter *pChr = GameServer()->m_apPlayers[From]->GetCharacter(); + if (pChr) { - chr->emote_type = EMOTE_HAPPY; - chr->emote_stop = server_tick() + server_tickspeed(); + pChr->m_EmoteType = EMOTE_HAPPY; + pChr->m_EmoteStop = Server()->Tick() + Server()->TickSpeed(); } } return false; } - if (dmg > 2) - game.create_sound(pos, SOUND_PLAYER_PAIN_LONG); + if (Dmg > 2) + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG); else - game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT); + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_SHORT); - emote_type = EMOTE_PAIN; - emote_stop = server_tick() + 500 * server_tickspeed() / 1000; + m_EmoteType = EMOTE_PAIN; + m_EmoteStop = Server()->Tick() + 500 * Server()->TickSpeed() / 1000; - // spawn blood? return true; } -void CHARACTER::snap(int snapping_client) +void CCharacter::Snap(int SnappingClient) { - if(networkclipped(snapping_client)) + if(NetworkClipped(SnappingClient)) return; - NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER)); + CNetObj_Character *Character = static_cast<CNetObj_Character *>(Server()->SnapNewItem(NETOBJTYPE_CHARACTER, m_pPlayer->GetCID(), sizeof(CNetObj_Character))); - // write down the core - if(game.world.paused) + // write down the m_Core + if(GameServer()->m_World.m_Paused) { // no dead reckoning when paused because the client doesn't know // how far to perform the reckoning - character->tick = 0; - core.write(character); + Character->m_Tick = 0; + m_Core.Write(Character); } else { - character->tick = reckoning_tick; - sendcore.write(character); + Character->m_Tick = m_ReckoningTick; + m_SendCore.Write(Character); } // set emote - if (emote_stop < server_tick()) + if (m_EmoteStop < Server()->Tick()) { - emote_type = EMOTE_NORMAL; - emote_stop = -1; + m_EmoteType = EMOTE_NORMAL; + m_EmoteStop = -1; } - character->emote = emote_type; + Character->m_Emote = m_EmoteType; - character->ammocount = 0; - character->health = 0; - character->armor = 0; + Character->m_AmmoCount = 0; + Character->m_Health = 0; + Character->m_Armor = 0; - character->weapon = active_weapon; - character->attacktick = attack_tick; + Character->m_Weapon = m_ActiveWeapon; + Character->m_AttackTick = m_AttackTick; - character->direction = input.direction; + Character->m_Direction = m_Input.m_Direction; - if(player->client_id == snapping_client) + if(m_pPlayer->GetCID() == SnappingClient) { - character->health = health; - character->armor = armor; - if(weapons[active_weapon].ammo > 0) - character->ammocount = weapons[active_weapon].ammo; + Character->m_Health = m_Health; + Character->m_Armor = m_Armor; + if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) + Character->m_AmmoCount = m_aWeapons[m_ActiveWeapon].m_Ammo; } - if (character->emote == EMOTE_NORMAL) + if (Character->m_Emote == EMOTE_NORMAL) { - if(250 - ((server_tick() - last_action)%(250)) < 5) - character->emote = EMOTE_BLINK; + if(250 - ((Server()->Tick() - m_LastAction)%(250)) < 5) + Character->m_Emote = EMOTE_BLINK; } - character->player_state = player_state; + Character->m_PlayerState = m_PlayerState; } diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h new file mode 100644 index 00000000..30c6586f --- /dev/null +++ b/src/game/server/entities/character.h @@ -0,0 +1,133 @@ +#ifndef GAME_SERVER_ENTITIES_CHARACTER_H +#define GAME_SERVER_ENTITIES_CHARACTER_H + +#include <game/server/entity.h> +#include <game/generated/server_data.h> +#include <game/generated/protocol.h> + +#include <game/gamecore.h> + +//character's size +const int g_CharPhysSize = 28; + +enum +{ + WEAPON_GAME = -3, // team switching etc + WEAPON_SELF = -2, // console kill command + WEAPON_WORLD = -1, // death tiles etc +}; + +class CCharacter : public CEntity +{ + MACRO_ALLOC_POOL_ID() + +public: + CCharacter(CGameWorld *pWorld); + + virtual void Reset(); + virtual void Destroy(); + virtual void Tick(); + virtual void TickDefered(); + virtual void Snap(int SnappingClient); + + bool IsGrounded(); + + void SetWeapon(int W); + void HandleWeaponSwitch(); + void DoWeaponSwitch(); + + void HandleWeapons(); + void HandleNinja(); + + void OnPredictedInput(CNetObj_PlayerInput *pNewInput); + void OnDirectInput(CNetObj_PlayerInput *pNewInput); + void FireWeapon(); + + void Die(int Killer, int Weapon); + bool TakeDamage(vec2 Force, int Dmg, int From, int Weapon); + + bool Spawn(class CPlayer *pPlayer, vec2 Pos); + bool Remove(); + + bool IncreaseHealth(int Amount); + bool IncreaseArmor(int Amount); + + bool GiveWeapon(int Weapon, int Ammo); + void GiveNinja(); + + void SetEmote(int Emote, int Tick); + + const bool IsAlive() { return m_Alive; } + class CPlayer *GetPlayer() { return m_pPlayer; } + +private: + // player controlling this character + class CPlayer *m_pPlayer; + + bool m_Alive; + + // weapon info + CEntity *m_apHitObjects[10]; + int m_NumObjectsHit; + + struct WeaponStat + { + int m_AmmoRegenStart; + int m_Ammo; + int m_Ammocost; + bool m_Got; + + } m_aWeapons[NUM_WEAPONS]; + + int m_ActiveWeapon; + int m_LastWeapon; + int m_QueuedWeapon; + + int m_ReloadTimer; + int m_AttackTick; + + int m_DamageTaken; + + int m_EmoteType; + int m_EmoteStop; + + // last tick that the player took any action ie some input + int m_LastAction; + + // these are non-heldback inputs + CNetObj_PlayerInput m_LatestPrevInput; + CNetObj_PlayerInput m_LatestInput; + + // input + CNetObj_PlayerInput m_PrevInput; + CNetObj_PlayerInput m_Input; + int m_NumInputs; + int m_Jumped; + + int m_DamageTakenTick; + + int m_Health; + int m_Armor; + + // ninja + struct + { + vec2 m_ActivationDir; + int m_ActivationTick; + int m_CurrentMoveTime; + + } m_Ninja; + + int m_PlayerState;// if the client is chatting, accessing a menu or so + + // the player core for the physics + CCharacterCore m_Core; + + // info for dead reckoning + int m_ReckoningTick; // tick that we are performing dead reckoning From + CCharacterCore m_SendCore; // core that we should send + CCharacterCore m_ReckoningCore; // the dead reckoning core + +}; + +#endif diff --git a/src/game/server/entities/character.hpp b/src/game/server/entities/character.hpp deleted file mode 100644 index bd2f7afe..00000000 --- a/src/game/server/entities/character.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_CHARACTER_H -#define GAME_SERVER_ENTITY_CHARACTER_H - -#include <game/server/entity.hpp> -#include <game/generated/gs_data.hpp> -#include <game/generated/g_protocol.hpp> - -#include <game/gamecore.hpp> - -enum -{ - WEAPON_GAME = -3, // team switching etc - WEAPON_SELF = -2, // console kill command - WEAPON_WORLD = -1, // death tiles etc -}; - -class CHARACTER : public ENTITY -{ - MACRO_ALLOC_POOL_ID() -public: - // player controlling this character - class PLAYER *player; - - bool alive; - - // weapon info - ENTITY *hitobjects[10]; - int numobjectshit; - struct WEAPONSTAT - { - int ammoregenstart; - int ammo; - int ammocost; - bool got; - } weapons[NUM_WEAPONS]; - - int active_weapon; - int last_weapon; - int queued_weapon; - - int reload_timer; - int attack_tick; - - int damage_taken; - - int emote_type; - int emote_stop; - - // TODO: clean this up - char skin_name[64]; - int use_custom_color; - int color_body; - int color_feet; - - int last_action; // last tick that the player took any action ie some input - - // these are non-heldback inputs - NETOBJ_PLAYER_INPUT latest_previnput; - NETOBJ_PLAYER_INPUT latest_input; - - // input - NETOBJ_PLAYER_INPUT previnput; - NETOBJ_PLAYER_INPUT input; - int num_inputs; - int jumped; - - int damage_taken_tick; - - int health; - int armor; - - // ninja - struct - { - vec2 activationdir; - int activationtick; - int currentmovetime; - } ninja; - - // - //int score; - int team; - int player_state; // if the client is chatting, accessing a menu or so - - // the player core for the physics - CHARACTER_CORE core; - - // info for dead reckoning - int reckoning_tick; // tick that we are performing dead reckoning from - CHARACTER_CORE sendcore; // core that we should send - CHARACTER_CORE reckoningcore; // the dead reckoning core - - // - CHARACTER(); - - virtual void reset(); - virtual void destroy(); - - bool is_grounded(); - - void set_weapon(int w); - - void handle_weaponswitch(); - void do_weaponswitch(); - - int handle_weapons(); - int handle_ninja(); - - void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); - void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); - void fire_weapon(); - - void die(int killer, int weapon); - - bool take_damage(vec2 force, int dmg, int from, int weapon); - - - bool spawn(PLAYER *player, vec2 pos, int team); - //bool init_tryspawn(int team); - bool remove(); - - static const int phys_size = 28; - - virtual void tick(); - virtual void tick_defered(); - virtual void snap(int snapping_client); - - bool increase_health(int amount); - bool increase_armor(int amount); -}; - -#endif diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp index 2c6fa0ff..6bc26074 100644 --- a/src/game/server/entities/laser.cpp +++ b/src/game/server/entities/laser.cpp @@ -1,115 +1,105 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "laser.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "laser.h" -////////////////////////////////////////////////// -// laser -////////////////////////////////////////////////// -LASER::LASER(vec2 pos, vec2 direction, float start_energy, int owner) -: ENTITY(NETOBJTYPE_LASER) +CLaser::CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner) +: CEntity(pGameWorld, NETOBJTYPE_LASER) { - this->pos = pos; - this->owner = owner; - energy = start_energy; - dir = direction; - bounces = 0; - do_bounce(); - - game.world.insert_entity(this); + m_Pos = Pos; + m_Owner = Owner; + m_Energy = StartEnergy; + m_Dir = Direction; + m_Bounces = 0; + m_EvalTick = 0; + GameWorld()->InsertEntity(this); + DoBounce(); } -bool LASER::hit_character(vec2 from, vec2 to) +bool CLaser::HitCharacter(vec2 From, vec2 To) { - vec2 at; - CHARACTER *owner_char = game.get_player_char(owner); - CHARACTER *hit = game.world.intersect_character(pos, to, 0.0f, at, owner_char); - if(!hit) + vec2 At; + CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *Hit = GameServer()->m_World.IntersectCharacter(m_Pos, To, 0.f, At, OwnerChar); + if(!Hit) return false; - this->from = from; - pos = at; - energy = -1; - hit->take_damage(vec2(0,0), tuning.laser_damage, owner, WEAPON_RIFLE); + m_From = From; + m_Pos = At; + m_Energy = -1; + Hit->TakeDamage(vec2(0.f, 0.f), GameServer()->Tuning()->m_LaserDamage, m_Owner, WEAPON_RIFLE); return true; } -void LASER::do_bounce() +void CLaser::DoBounce() { - eval_tick = server_tick(); + m_EvalTick = Server()->Tick(); - if(energy < 0) + if(m_Energy < 0) { - //dbg_msg("laser", "%d removed", server_tick()); - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); return; } - vec2 to = pos + dir*energy; - vec2 org_to = to; + vec2 To = m_Pos + m_Dir * m_Energy; + vec2 OrgTo = To; - if(col_intersect_line(pos, to, 0x0, &to)) + if(GameServer()->Collision()->IntersectLine(m_Pos, To, 0x0, &To)) { - if(!hit_character(pos, to)) + if(!HitCharacter(m_Pos, To)) { // intersected - from = pos; - pos = to; + m_From = m_Pos; + m_Pos = To; - vec2 temp_pos = pos; - vec2 temp_dir = dir*4.0f; + vec2 TempPos = m_Pos; + vec2 TempDir = m_Dir * 4.0f; - move_point(&temp_pos, &temp_dir, 1.0f, 0); - pos = temp_pos; - dir = normalize(temp_dir); + GameServer()->Collision()->MovePoint(&TempPos, &TempDir, 1.0f, 0); + m_Pos = TempPos; + m_Dir = normalize(TempDir); - energy -= distance(from, pos) + tuning.laser_bounce_cost; - bounces++; + m_Energy -= distance(m_From, m_Pos) + GameServer()->Tuning()->m_LaserBounceCost; + m_Bounces++; - if(bounces > tuning.laser_bounce_num) - energy = -1; + if(m_Bounces > GameServer()->Tuning()->m_LaserBounceNum) + m_Energy = -1; - game.create_sound(pos, SOUND_RIFLE_BOUNCE); + GameServer()->CreateSound(m_Pos, SOUND_RIFLE_BOUNCE); } } else { - if(!hit_character(pos, to)) + if(!HitCharacter(m_Pos, To)) { - from = pos; - pos = to; - energy = -1; + m_From = m_Pos; + m_Pos = To; + m_Energy = -1; } } - - //dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y); } -void LASER::reset() +void CLaser::Reset() { - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } -void LASER::tick() +void CLaser::Tick() { - if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f) - { - do_bounce(); - } - + if(Server()->Tick() > m_EvalTick+(Server()->TickSpeed()*GameServer()->Tuning()->m_LaserBounceDelay)/1000.0f) + DoBounce(); } -void LASER::snap(int snapping_client) +void CLaser::Snap(int SnappingClient) { - if(networkclipped(snapping_client)) + if(NetworkClipped(SnappingClient)) return; - NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER)); - obj->x = (int)pos.x; - obj->y = (int)pos.y; - obj->from_x = (int)from.x; - obj->from_y = (int)from.y; - obj->start_tick = eval_tick; + CNetObj_Laser *pObj = static_cast<CNetObj_Laser *>(Server()->SnapNewItem(NETOBJTYPE_LASER, m_Id, sizeof(CNetObj_Laser))); + pObj->m_X = (int)m_Pos.x; + pObj->m_Y = (int)m_Pos.y; + pObj->m_FromX = (int)m_From.x; + pObj->m_FromY = (int)m_From.y; + pObj->m_StartTick = m_EvalTick; } diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h new file mode 100644 index 00000000..040cfb4c --- /dev/null +++ b/src/game/server/entities/laser.h @@ -0,0 +1,28 @@ +#ifndef GAME_SERVER_ENTITIES_LASER_H +#define GAME_SERVER_ENTITIES_LASER_H + +#include <game/server/entity.h> + +class CLaser : public CEntity +{ +public: + CLaser(CGameWorld *pGameWorld, vec2 Pos, vec2 Direction, float StartEnergy, int Owner); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +protected: + bool HitCharacter(vec2 From, vec2 To); + void DoBounce(); + +private: + vec2 m_From; + vec2 m_Dir; + float m_Energy; + int m_Bounces; + int m_EvalTick; + int m_Owner; +}; + +#endif diff --git a/src/game/server/entities/laser.hpp b/src/game/server/entities/laser.hpp deleted file mode 100644 index aa4c2284..00000000 --- a/src/game/server/entities/laser.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_LASER_H -#define GAME_SERVER_ENTITY_LASER_H - -#include <game/server/entity.hpp> - -class CHARACTER; - -class LASER : public ENTITY -{ - vec2 from; - vec2 dir; - float energy; - int bounces; - int eval_tick; - int owner; - - bool hit_character(vec2 from, vec2 to); - void do_bounce(); - -public: - - LASER(vec2 pos, vec2 direction, float start_energy, int owner); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp index 436282cc..9798e2c3 100644 --- a/src/game/server/entities/pickup.cpp +++ b/src/game/server/entities/pickup.cpp @@ -1,143 +1,129 @@ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "pickup.hpp" +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "pickup.h" -////////////////////////////////////////////////// -// pickup -////////////////////////////////////////////////// -PICKUP::PICKUP(int _type, int _subtype) -: ENTITY(NETOBJTYPE_PICKUP) +CPickup::CPickup(CGameWorld *pGameWorld, int Type, int SubType) +: CEntity(pGameWorld, NETOBJTYPE_PICKUP) { - type = _type; - subtype = _subtype; - proximity_radius = phys_size; + m_Type = Type; + m_Subtype = SubType; + m_ProximityRadius = PickupPhysSize; - reset(); - - // TODO: should this be done here? - game.world.insert_entity(this); + Reset(); + + GameWorld()->InsertEntity(this); } -void PICKUP::reset() +void CPickup::Reset() { - if (data->pickups[type].spawndelay > 0) - spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay; + if (g_pData->m_aPickups[m_Type].m_Spawndelay > 0) + m_SpawnTick = Server()->Tick() + Server()->TickSpeed() * g_pData->m_aPickups[m_Type].m_Spawndelay; else - spawntick = -1; + m_SpawnTick = -1; } -void PICKUP::tick() +void CPickup::Tick() { // wait for respawn - if(spawntick > 0) + if(m_SpawnTick > 0) { - if(server_tick() > spawntick) + if(Server()->Tick() > m_SpawnTick) { // respawn - spawntick = -1; + m_SpawnTick = -1; - if(type == POWERUP_WEAPON) - game.create_sound(pos, SOUND_WEAPON_SPAWN); + if(m_Type == POWERUP_WEAPON) + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SPAWN); } else return; } // Check if a player intersected us - CHARACTER *chr = game.world.closest_character(pos, 20.0f, 0); - if(chr) + CCharacter *pChr = GameServer()->m_World.ClosestCharacter(m_Pos, 20.0f, 0); + if(pChr && pChr->IsAlive()) { // player picked us up, is someone was hooking us, let them go - int respawntime = -1; - switch (type) + int RespawnTime = -1; + switch (m_Type) { - case POWERUP_HEALTH: - if(chr->increase_health(1)) - { - game.create_sound(pos, SOUND_PICKUP_HEALTH); - respawntime = data->pickups[type].respawntime; - } - break; - case POWERUP_ARMOR: - if(chr->increase_armor(1)) - { - game.create_sound(pos, SOUND_PICKUP_ARMOR); - respawntime = data->pickups[type].respawntime; - } - break; + case POWERUP_HEALTH: + if(pChr->IncreaseHealth(1)) + { + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_HEALTH); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + } + break; + + case POWERUP_ARMOR: + if(pChr->IncreaseArmor(1)) + { + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_ARMOR); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + } + break; - case POWERUP_WEAPON: - if(subtype >= 0 && subtype < NUM_WEAPONS) - { - if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got) + case POWERUP_WEAPON: + if(m_Subtype >= 0 && m_Subtype < NUM_WEAPONS) { - chr->weapons[subtype].got = true; - chr->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, chr->weapons[subtype].ammo + 10); - respawntime = data->pickups[type].respawntime; + if(pChr->GiveWeapon(m_Subtype, 10)) + { + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; - // TODO: data compiler should take care of stuff like this - if(subtype == WEAPON_GRENADE) - game.create_sound(pos, SOUND_PICKUP_GRENADE); - else if(subtype == WEAPON_SHOTGUN) - game.create_sound(pos, SOUND_PICKUP_SHOTGUN); - else if(subtype == WEAPON_RIFLE) - game.create_sound(pos, SOUND_PICKUP_SHOTGUN); + if(m_Subtype == WEAPON_GRENADE) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_GRENADE); + else if(m_Subtype == WEAPON_SHOTGUN) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_SHOTGUN); + else if(m_Subtype == WEAPON_RIFLE) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_SHOTGUN); - if(chr->player) - game.send_weapon_pickup(chr->player->client_id, subtype); + if(pChr->GetPlayer()) + GameServer()->SendWeaponPickup(pChr->GetPlayer()->GetCID(), m_Subtype); + } } - } - break; - case POWERUP_NINJA: - { - // activate ninja on target player - chr->ninja.activationtick = server_tick(); - chr->weapons[WEAPON_NINJA].got = true; - chr->weapons[WEAPON_NINJA].ammo = -1; - chr->last_weapon = chr->active_weapon; - chr->active_weapon = WEAPON_NINJA; - respawntime = data->pickups[type].respawntime; - game.create_sound(pos, SOUND_PICKUP_NINJA); - - // loop through all players, setting their emotes - ENTITY *ents[64]; - int num = game.world.find_entities(vec2(0, 0), 1000000, ents, 64, NETOBJTYPE_CHARACTER); - for (int i = 0; i < num; i++) + break; + + case POWERUP_NINJA: { - CHARACTER *c = (CHARACTER *)ents[i]; - if (c != chr) + // activate ninja on target player + pChr->GiveNinja(); + RespawnTime = g_pData->m_aPickups[m_Type].m_Respawntime; + + // loop through all players, setting their emotes + CEntity *apEnts[64]; + int Num = GameServer()->m_World.FindEntities(vec2(0, 0), 1000000, apEnts, 64, NETOBJTYPE_CHARACTER); + + for (int i = 0; i < Num; ++i) { - c->emote_type = EMOTE_SURPRISE; - c->emote_stop = server_tick() + server_tickspeed(); + CCharacter *pC = static_cast<CCharacter *>(apEnts[i]); + if (pC != pChr) + pC->SetEmote(EMOTE_SURPRISE, Server()->Tick() + Server()->TickSpeed()); } - } - chr->emote_type = EMOTE_ANGRY; - chr->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000; + pChr->SetEmote(EMOTE_ANGRY, Server()->Tick() + 1200 * Server()->TickSpeed() / 1000); + break; + } + default: break; - } - default: - break; }; - if(respawntime >= 0) + if(RespawnTime >= 0) { dbg_msg("game", "pickup player='%d:%s' item=%d/%d", - chr->player->client_id, server_clientname(chr->player->client_id), type, subtype); - spawntick = server_tick() + server_tickspeed() * respawntime; + pChr->GetPlayer()->GetCID(), Server()->ClientName(pChr->GetPlayer()->GetCID()), m_Type, m_Subtype); + m_SpawnTick = Server()->Tick() + Server()->TickSpeed() * RespawnTime; } } } -void PICKUP::snap(int snapping_client) +void CPickup::Snap(int SnappingClient) { - if(spawntick != -1) + if(m_SpawnTick != -1 || NetworkClipped(SnappingClient)) return; - NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP)); - up->x = (int)pos.x; - up->y = (int)pos.y; - up->type = type; // TODO: two diffrent types? what gives? - up->subtype = subtype; + CNetObj_Pickup *pP = static_cast<CNetObj_Pickup *>(Server()->SnapNewItem(NETOBJTYPE_PICKUP, m_Id, sizeof(CNetObj_Pickup))); + pP->m_X = (int)m_Pos.x; + pP->m_Y = (int)m_Pos.y; + pP->m_Type = m_Type; + pP->m_Subtype = m_Subtype; } diff --git a/src/game/server/entities/pickup.h b/src/game/server/entities/pickup.h new file mode 100644 index 00000000..c076464c --- /dev/null +++ b/src/game/server/entities/pickup.h @@ -0,0 +1,23 @@ +#ifndef GAME_SERVER_ENTITIES_PICKUP_H +#define GAME_SERVER_ENTITIES_PICKUP_H + +#include <game/server/entity.h> + +const int PickupPhysSize = 14; + +class CPickup : public CEntity +{ +public: + CPickup(CGameWorld *pGameWorld, int Type, int SubType = 0); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +private: + int m_Type; + int m_Subtype; + int m_SpawnTick; +}; + +#endif diff --git a/src/game/server/entities/pickup.hpp b/src/game/server/entities/pickup.hpp deleted file mode 100644 index cd480d92..00000000 --- a/src/game/server/entities/pickup.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_PICKUP_H -#define GAME_SERVER_ENTITY_PICKUP_H - -#include <game/server/entity.hpp> - -// TODO: move to seperate file -class PICKUP : public ENTITY -{ -public: - static const int phys_size = 14; - - int type; - int subtype; // weapon type for instance? - int spawntick; - PICKUP(int _type, int _subtype = 0); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp index 2a8de766..18652ba1 100644 --- a/src/game/server/entities/projectile.cpp +++ b/src/game/server/entities/projectile.cpp @@ -1,105 +1,102 @@ -#include <engine/e_server_interface.h> -#include <game/generated/g_protocol.hpp> -#include <game/server/gamecontext.hpp> -#include "projectile.hpp" +#include <game/generated/protocol.h> +#include <game/server/gamecontext.h> +#include "projectile.h" - -////////////////////////////////////////////////// -// projectile -////////////////////////////////////////////////// -PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span, - int damage, int flags, float force, int sound_impact, int weapon) -: ENTITY(NETOBJTYPE_PROJECTILE) +CProjectile::CProjectile(CGameWorld *pGameWorld, int Type, int Owner, vec2 Pos, vec2 Dir, int Span, + int Damage, bool Explosive, float Force, int SoundImpact, int Weapon) +: CEntity(pGameWorld, NETOBJTYPE_PROJECTILE) { - this->type = type; - this->pos = pos; - this->direction = dir; - this->lifespan = span; - this->owner = owner; - this->flags = flags; - this->force = force; - this->damage = damage; - this->sound_impact = sound_impact; - this->weapon = weapon; - this->bounce = 0; - this->start_tick = server_tick(); - game.world.insert_entity(this); + m_Type = Type; + m_Pos = Pos; + m_Direction = Dir; + m_LifeSpan = Span; + m_Owner = Owner; + m_Force = Force; + m_Damage = Damage; + m_SoundImpact = SoundImpact; + m_Weapon = Weapon; + m_StartTick = Server()->Tick(); + m_Explosive = Explosive; + + GameWorld()->InsertEntity(this); } -void PROJECTILE::reset() +void CProjectile::Reset() { - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } -vec2 PROJECTILE::get_pos(float time) +vec2 CProjectile::GetPos(float Time) { - float curvature = 0; - float speed = 0; - if(type == WEAPON_GRENADE) - { - curvature = tuning.grenade_curvature; - speed = tuning.grenade_speed; - } - else if(type == WEAPON_SHOTGUN) - { - curvature = tuning.shotgun_curvature; - speed = tuning.shotgun_speed; - } - else if(type == WEAPON_GUN) + float Curvature = 0; + float Speed = 0; + + switch(m_Type) { - curvature = tuning.gun_curvature; - speed = tuning.gun_speed; + case WEAPON_GRENADE: + Curvature = GameServer()->Tuning()->m_GrenadeCurvature; + Speed = GameServer()->Tuning()->m_GrenadeSpeed; + break; + + case WEAPON_SHOTGUN: + Curvature = GameServer()->Tuning()->m_ShotgunCurvature; + Speed = GameServer()->Tuning()->m_ShotgunSpeed; + break; + + case WEAPON_GUN: + Curvature = GameServer()->Tuning()->m_GunCurvature; + Speed = GameServer()->Tuning()->m_GunSpeed; + break; } - return calc_pos(pos, direction, curvature, speed, time); + return CalcPos(m_Pos, m_Direction, Curvature, Speed, Time); } -void PROJECTILE::tick() +void CProjectile::Tick() { - - float pt = (server_tick()-start_tick-1)/(float)server_tickspeed(); - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); - vec2 prevpos = get_pos(pt); - vec2 curpos = get_pos(ct); + float Pt = (Server()->Tick()-m_StartTick-1)/(float)Server()->TickSpeed(); + float Ct = (Server()->Tick()-m_StartTick)/(float)Server()->TickSpeed(); + vec2 PrevPos = GetPos(Pt); + vec2 CurPos = GetPos(Ct); + int Collide = GameServer()->Collision()->IntersectLine(PrevPos, CurPos, &CurPos, 0); + CCharacter *OwnerChar = GameServer()->GetPlayerChar(m_Owner); + CCharacter *TargetChr = GameServer()->m_World.IntersectCharacter(PrevPos, CurPos, 6.0f, CurPos, OwnerChar); - lifespan--; + m_LifeSpan--; - int collide = col_intersect_line(prevpos, curpos, &curpos, 0); - //int collide = col_check_point((int)curpos.x, (int)curpos.y); - CHARACTER *ownerchar = game.get_player_char(owner); - CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, ownerchar); - if(targetchr || collide || lifespan < 0) + if(TargetChr || Collide || m_LifeSpan < 0) { - if(lifespan >= 0 || weapon == WEAPON_GRENADE) - game.create_sound(curpos, sound_impact); + if(m_LifeSpan >= 0 || m_Weapon == WEAPON_GRENADE) + GameServer()->CreateSound(CurPos, m_SoundImpact); - if(flags & PROJECTILE_FLAGS_EXPLODE) - game.create_explosion(curpos, owner, weapon, false); - else if(targetchr) - targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon); + if(m_Explosive) + GameServer()->CreateExplosion(CurPos, m_Owner, m_Weapon, false); + + else if(TargetChr) + TargetChr->TakeDamage(m_Direction * max(0.001f, m_Force), m_Damage, m_Owner, m_Weapon); - game.world.destroy_entity(this); + GameServer()->m_World.DestroyEntity(this); } } -void PROJECTILE::fill_info(NETOBJ_PROJECTILE *proj) +void CProjectile::FillInfo(CNetObj_Projectile *pProj) { - proj->x = (int)pos.x; - proj->y = (int)pos.y; - proj->vx = (int)(direction.x*100.0f); - proj->vy = (int)(direction.y*100.0f); - proj->start_tick = start_tick; - proj->type = type; + pProj->m_X = (int)m_Pos.x; + pProj->m_Y = (int)m_Pos.y; + pProj->m_VelX = (int)(m_Direction.x*100.0f); + pProj->m_VelY = (int)(m_Direction.y*100.0f); + pProj->m_StartTick = m_StartTick; + pProj->m_Type = m_Type; } -void PROJECTILE::snap(int snapping_client) +void CProjectile::Snap(int SnappingClient) { - float ct = (server_tick()-start_tick)/(float)server_tickspeed(); + float Ct = (Server()->Tick()-m_StartTick)/(float)Server()->TickSpeed(); - if(networkclipped(snapping_client, get_pos(ct))) + if(NetworkClipped(SnappingClient, GetPos(Ct))) return; - NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE)); - fill_info(proj); + CNetObj_Projectile *pProj = static_cast<CNetObj_Projectile *>(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, m_Id, sizeof(CNetObj_Projectile))); + FillInfo(pProj); } diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h new file mode 100644 index 00000000..87f4f6c3 --- /dev/null +++ b/src/game/server/entities/projectile.h @@ -0,0 +1,30 @@ +#ifndef GAME_SERVER_ENTITIES_PROJECTILE_H +#define GAME_SERVER_ENTITIES_PROJECTILE_H + +class CProjectile : public CEntity +{ +public: + CProjectile(CGameWorld *pGameWorld, int Type, int Owner, vec2 Pos, vec2 Dir, int Span, + int Damage, bool Explosive, float Force, int SoundImpact, int Weapon); + + vec2 GetPos(float Time); + void FillInfo(CNetObj_Projectile *pProj); + + virtual void Reset(); + virtual void Tick(); + virtual void Snap(int SnappingClient); + +private: + vec2 m_Direction; + int m_LifeSpan; + int m_Owner; + int m_Type; + int m_Damage; + int m_SoundImpact; + int m_Weapon; + float m_Force; + int m_StartTick; + bool m_Explosive; +}; + +#endif diff --git a/src/game/server/entities/projectile.hpp b/src/game/server/entities/projectile.hpp deleted file mode 100644 index a5c3b88f..00000000 --- a/src/game/server/entities/projectile.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#ifndef GAME_SERVER_ENTITY_PROJECTILE_H -#define GAME_SERVER_ENTITY_PROJECTILE_H - -class PROJECTILE : public ENTITY -{ -public: - enum - { - PROJECTILE_FLAGS_EXPLODE = 1 << 0, - }; - - vec2 direction; - int lifespan; - int owner; - int type; - int flags; - int damage; - int sound_impact; - int weapon; - int bounce; - float force; - int start_tick; - - PROJECTILE(int type, int owner, vec2 pos, vec2 vel, int span, - int damage, int flags, float force, int sound_impact, int weapon); - - vec2 get_pos(float time); - void fill_info(NETOBJ_PROJECTILE *proj); - - virtual void reset(); - virtual void tick(); - virtual void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/entity.cpp b/src/game/server/entity.cpp index 8e3345ab..d17c3fab 100644 --- a/src/game/server/entity.cpp +++ b/src/game/server/entity.cpp @@ -1,49 +1,50 @@ -#include <engine/e_server_interface.h> -#include "entity.hpp" -#include "gamecontext.hpp" +#include "entity.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // Entity ////////////////////////////////////////////////// -ENTITY::ENTITY(int objtype) +CEntity::CEntity(CGameWorld *pGameWorld, int ObjType) { - this->objtype = objtype; - pos = vec2(0,0); - proximity_radius = 0; + m_pGameWorld = pGameWorld; + + m_Objtype = ObjType; + m_Pos = vec2(0,0); + m_ProximityRadius = 0; - marked_for_destroy = false; - id = snap_new_id(); + m_MarkedForDestroy = false; + m_Id = Server()->SnapNewID(); - next_entity = 0; - prev_entity = 0; - prev_type_entity = 0; - next_type_entity = 0; + m_pNextEntity = 0; + m_pPrevEntity = 0; + m_pPrevTypeEntity = 0; + m_pNextTypeEntity = 0; } -ENTITY::~ENTITY() +CEntity::~CEntity() { - game.world.remove_entity(this); - snap_free_id(id); + GameWorld()->RemoveEntity(this); + Server()->SnapFreeID(m_Id); } -int ENTITY::networkclipped(int snapping_client) +int CEntity::NetworkClipped(int SnappingClient) { - return networkclipped(snapping_client, pos); + return NetworkClipped(SnappingClient, m_Pos); } -int ENTITY::networkclipped(int snapping_client, vec2 check_pos) +int CEntity::NetworkClipped(int SnappingClient, vec2 CheckPos) { - if(snapping_client == -1) + if(SnappingClient == -1) return 0; - float dx = game.players[snapping_client]->view_pos.x-check_pos.x; - float dy = game.players[snapping_client]->view_pos.y-check_pos.y; + float dx = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.x-CheckPos.x; + float dy = GameServer()->m_apPlayers[SnappingClient]->m_ViewPos.y-CheckPos.y; - if(fabs(dx) > 1000.0f || fabs(dy) > 800.0f) + if(absolute(dx) > 1000.0f || absolute(dy) > 800.0f) return 1; - if(distance(game.players[snapping_client]->view_pos, check_pos) > 1100.0f) + if(distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, CheckPos) > 1100.0f) return 1; return 0; } diff --git a/src/game/server/entity.hpp b/src/game/server/entity.h index debe57b6..b7fd3d94 100644 --- a/src/game/server/entity.hpp +++ b/src/game/server/entity.h @@ -2,101 +2,109 @@ #define GAME_SERVER_ENTITY_H #include <new> -#include <base/vmath.hpp> +#include <base/vmath.h> +#include <game/server/gameworld.h> #define MACRO_ALLOC_HEAP() \ public: \ - void *operator new(size_t size) \ + void *operator new(size_t Size) \ { \ - void *p = mem_alloc(size, 1); \ + void *p = mem_alloc(Size, 1); \ /*dbg_msg("", "++ %p %d", p, size);*/ \ - mem_zero(p, size); \ + mem_zero(p, Size); \ return p; \ } \ - void operator delete(void *p) \ + void operator delete(void *pPtr) \ { \ /*dbg_msg("", "-- %p", p);*/ \ - mem_free(p); \ + mem_free(pPtr); \ } \ private: #define MACRO_ALLOC_POOL_ID() \ public: \ - void *operator new(size_t size, int id); \ + void *operator new(size_t Size, int id); \ void operator delete(void *p); \ private: -#define MACRO_ALLOC_POOL_ID_IMPL(POOLTYPE, poolsize) \ - static char pool_data_##POOLTYPE[poolsize][sizeof(POOLTYPE)] = {{0}}; \ - static int pool_used_##POOLTYPE[poolsize] = {0}; \ - void *POOLTYPE::operator new(size_t size, int id) \ +#define MACRO_ALLOC_POOL_ID_IMPL(POOLTYPE, PoolSize) \ + static char ms_PoolData##POOLTYPE[PoolSize][sizeof(POOLTYPE)] = {{0}}; \ + static int ms_PoolUsed##POOLTYPE[PoolSize] = {0}; \ + void *POOLTYPE::operator new(size_t Size, int id) \ { \ - dbg_assert(sizeof(POOLTYPE) == size, "size error"); \ - dbg_assert(!pool_used_##POOLTYPE[id], "already used"); \ + dbg_assert(sizeof(POOLTYPE) == Size, "size error"); \ + dbg_assert(!ms_PoolUsed##POOLTYPE[id], "already used"); \ /*dbg_msg("pool", "++ %s %d", #POOLTYPE, id);*/ \ - pool_used_##POOLTYPE[id] = 1; \ - mem_zero(pool_data_##POOLTYPE[id], size); \ - return pool_data_##POOLTYPE[id]; \ + ms_PoolUsed##POOLTYPE[id] = 1; \ + mem_zero(ms_PoolData##POOLTYPE[id], Size); \ + return ms_PoolData##POOLTYPE[id]; \ } \ void POOLTYPE::operator delete(void *p) \ { \ - int id = (POOLTYPE*)p - (POOLTYPE*)pool_data_##POOLTYPE; \ - dbg_assert(pool_used_##POOLTYPE[id], "not used"); \ + int id = (POOLTYPE*)p - (POOLTYPE*)ms_PoolData##POOLTYPE; \ + dbg_assert(ms_PoolUsed##POOLTYPE[id], "not used"); \ /*dbg_msg("pool", "-- %s %d", #POOLTYPE, id);*/ \ - pool_used_##POOLTYPE[id] = 0; \ - mem_zero(pool_data_##POOLTYPE[id], sizeof(POOLTYPE)); \ + ms_PoolUsed##POOLTYPE[id] = 0; \ + mem_zero(ms_PoolData##POOLTYPE[id], sizeof(POOLTYPE)); \ } /* Class: Entity Basic entity class. */ -class ENTITY +class CEntity { MACRO_ALLOC_HEAP() private: - friend class GAMEWORLD; // thy these? - ENTITY *prev_entity; - ENTITY *next_entity; + friend class CGameWorld; // thy these? + CEntity *m_pPrevEntity; + CEntity *m_pNextEntity; - ENTITY *prev_type_entity; - ENTITY *next_type_entity; + CEntity *m_pPrevTypeEntity; + CEntity *m_pNextTypeEntity; + + class CGameWorld *m_pGameWorld; protected: - bool marked_for_destroy; - int id; - int objtype; + bool m_MarkedForDestroy; + int m_Id; + int m_Objtype; public: - ENTITY(int objtype); - virtual ~ENTITY(); + CEntity(CGameWorld *pGameWorld, int Objtype); + virtual ~CEntity(); + + class CGameWorld *GameWorld() { return m_pGameWorld; } + class CGameContext *GameServer() { return GameWorld()->GameServer(); } + class IServer *Server() { return GameWorld()->Server(); } + - ENTITY *typenext() { return next_type_entity; } - ENTITY *typeprev() { return prev_type_entity; } + CEntity *TypeNext() { return m_pNextTypeEntity; } + CEntity *TypePrev() { return m_pPrevTypeEntity; } /* Function: destroy Destorys the entity. */ - virtual void destroy() { delete this; } + virtual void Destroy() { delete this; } /* Function: reset Called when the game resets the map. Puts the entity back to it's starting state or perhaps destroys it. */ - virtual void reset() {} + virtual void Reset() {} /* Function: tick Called progress the entity to the next tick. Updates and moves the entity to it's new state and position. */ - virtual void tick() {} + virtual void Tick() {} /* Function: tick_defered Called after all entities tick() function has been called. */ - virtual void tick_defered() {} + virtual void TickDefered() {} /* Function: snap @@ -109,7 +117,7 @@ public: snapshot of everything in the game for demo recording. */ - virtual void snap(int snapping_client) {} + virtual void Snap(int SnappingClient) {} /* Function: networkclipped(int snapping_client) @@ -125,21 +133,21 @@ public: Returns: Non-zero if the entity doesn't have to be in the snapshot. */ - int networkclipped(int snapping_client); - int networkclipped(int snapping_client, vec2 check_pos); + int NetworkClipped(int SnappingClient); + int NetworkClipped(int SnappingClient, vec2 CheckPos); /* Variable: proximity_radius Contains the physical size of the entity. */ - float proximity_radius; + float m_ProximityRadius; /* Variable: pos Contains the current posititon of the entity. */ - vec2 pos; + vec2 m_Pos; }; #endif diff --git a/src/game/server/eventhandler.cpp b/src/game/server/eventhandler.cpp index 761eaf2c..48b6689e 100644 --- a/src/game/server/eventhandler.cpp +++ b/src/game/server/eventhandler.cpp @@ -1,48 +1,54 @@ -#include "eventhandler.hpp" -#include "gamecontext.hpp" +#include "eventhandler.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // Event handler ////////////////////////////////////////////////// -EVENTHANDLER::EVENTHANDLER() +CEventHandler::CEventHandler() { - clear(); + m_pGameServer = 0; + Clear(); } -void *EVENTHANDLER::create(int type, int size, int mask) +void CEventHandler::SetGameServer(CGameContext *pGameServer) { - if(num_events == MAX_EVENTS) + m_pGameServer = pGameServer; +} + +void *CEventHandler::Create(int Type, int Size, int Mask) +{ + if(m_NumEvents == MAX_EVENTS) return 0; - if(current_offset+size >= MAX_DATASIZE) + if(m_CurrentOffset+Size >= MAX_DATASIZE) return 0; - void *p = &data[current_offset]; - offsets[num_events] = current_offset; - types[num_events] = type; - sizes[num_events] = size; - client_masks[num_events] = mask; - current_offset += size; - num_events++; + void *p = &m_aData[m_CurrentOffset]; + m_aOffsets[m_NumEvents] = m_CurrentOffset; + m_aTypes[m_NumEvents] = Type; + m_aSizes[m_NumEvents] = Size; + m_aClientMasks[m_NumEvents] = Mask; + m_CurrentOffset += Size; + m_NumEvents++; return p; } -void EVENTHANDLER::clear() +void CEventHandler::Clear() { - num_events = 0; - current_offset = 0; + m_NumEvents = 0; + m_CurrentOffset = 0; } -void EVENTHANDLER::snap(int snapping_client) +void CEventHandler::Snap(int SnappingClient) { - for(int i = 0; i < num_events; i++) + for(int i = 0; i < m_NumEvents; i++) { - if(snapping_client == -1 || cmask_is_set(client_masks[i], snapping_client)) + if(SnappingClient == -1 || CmaskIsSet(m_aClientMasks[i], SnappingClient)) { - NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]]; - if(snapping_client == -1 || distance(game.players[snapping_client]->view_pos, vec2(ev->x, ev->y)) < 1500.0f) + NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&m_aData[m_aOffsets[i]]; + if(SnappingClient == -1 || distance(GameServer()->m_apPlayers[SnappingClient]->m_ViewPos, vec2(ev->m_X, ev->m_Y)) < 1500.0f) { - void *d = snap_new_item(types[i], i, sizes[i]); - mem_copy(d, &data[offsets[i]], sizes[i]); + void *d = GameServer()->Server()->SnapNewItem(m_aTypes[i], i, m_aSizes[i]); + mem_copy(d, &m_aData[m_aOffsets[i]], m_aSizes[i]); } } } diff --git a/src/game/server/eventhandler.h b/src/game/server/eventhandler.h new file mode 100644 index 00000000..3833efe0 --- /dev/null +++ b/src/game/server/eventhandler.h @@ -0,0 +1,30 @@ +#ifndef GAME_SERVER_EVENTHANDLER_H +#define GAME_SERVER_EVENTHANDLER_H + +// +class CEventHandler +{ + static const int MAX_EVENTS = 128; + static const int MAX_DATASIZE = 128*64; + + int m_aTypes[MAX_EVENTS]; // TODO: remove some of these arrays + int m_aOffsets[MAX_EVENTS]; + int m_aSizes[MAX_EVENTS]; + int m_aClientMasks[MAX_EVENTS]; + char m_aData[MAX_DATASIZE]; + + class CGameContext *m_pGameServer; + + int m_CurrentOffset; + int m_NumEvents; +public: + CGameContext *GameServer() const { return m_pGameServer; } + void SetGameServer(CGameContext *pGameServer); + + CEventHandler(); + void *Create(int Type, int Size, int Mask = -1); + void Clear(); + void Snap(int SnappingClient); +}; + +#endif diff --git a/src/game/server/eventhandler.hpp b/src/game/server/eventhandler.hpp deleted file mode 100644 index 4d513154..00000000 --- a/src/game/server/eventhandler.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef GAME_SERVER_EVENTHANDLER_H -#define GAME_SERVER_EVENTHANDLER_H - -// -class EVENTHANDLER -{ - static const int MAX_EVENTS = 128; - static const int MAX_DATASIZE = 128*64; - - int types[MAX_EVENTS]; // TODO: remove some of these arrays - int offsets[MAX_EVENTS]; - int sizes[MAX_EVENTS]; - int client_masks[MAX_EVENTS]; - char data[MAX_DATASIZE]; - - int current_offset; - int num_events; -public: - EVENTHANDLER(); - void *create(int type, int size, int mask = -1); - void clear(); - void snap(int snapping_client); -}; - -#endif diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 736d437f..795bb65f 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1,99 +1,143 @@ -#include <string.h> #include <new> -#include <engine/e_server_interface.h> -#include "gamecontext.hpp" +#include <base/math.h> +#include <engine/shared/config.h> +#include <engine/map.h> +#include <engine/console.h> +#include "gamecontext.h" +#include <game/version.h> +#include <game/collision.h> +#include <game/gamecore.h> +#include "gamemodes/dm.h" +#include "gamemodes/tdm.h" +#include "gamemodes/ctf.h" +#include "gamemodes/mod.h" -GAMECONTEXT game; +enum +{ + RESET, + NO_RESET +}; -GAMECONTEXT::GAMECONTEXT() +void CGameContext::Construct(int Resetting) { + m_Resetting = 0; + m_pServer = 0; + for(int i = 0; i < MAX_CLIENTS; i++) - players[i] = 0; + m_apPlayers[i] = 0; - controller = 0; - vote_closetime = 0; + m_pController = 0; + m_VoteCloseTime = 0; + m_pVoteOptionFirst = 0; + m_pVoteOptionLast = 0; + + if(Resetting==NO_RESET) + m_pVoteOptionHeap = new CHeap(); +} + +CGameContext::CGameContext(int Resetting) +{ + Construct(Resetting); +} + +CGameContext::CGameContext() +{ + Construct(NO_RESET); } -GAMECONTEXT::~GAMECONTEXT() +CGameContext::~CGameContext() { for(int i = 0; i < MAX_CLIENTS; i++) - delete players[i]; + delete m_apPlayers[i]; + if(!m_Resetting) + delete m_pVoteOptionHeap; } -void GAMECONTEXT::clear() +void CGameContext::Clear() { - this->~GAMECONTEXT(); + CHeap *pVoteOptionHeap = m_pVoteOptionHeap; + CVoteOption *pVoteOptionFirst = m_pVoteOptionFirst; + CVoteOption *pVoteOptionLast = m_pVoteOptionLast; + CTuningParams Tuning = m_Tuning; + + m_Resetting = true; + this->~CGameContext(); mem_zero(this, sizeof(*this)); - new (this) GAMECONTEXT(); + new (this) CGameContext(RESET); + + m_pVoteOptionHeap = pVoteOptionHeap; + m_pVoteOptionFirst = pVoteOptionFirst; + m_pVoteOptionLast = pVoteOptionLast; + m_Tuning = Tuning; } -class CHARACTER *GAMECONTEXT::get_player_char(int client_id) +class CCharacter *CGameContext::GetPlayerChar(int ClientId) { - if(client_id < 0 || client_id >= MAX_CLIENTS || !players[client_id]) + if(ClientId < 0 || ClientId >= MAX_CLIENTS || !m_apPlayers[ClientId]) return 0; - return players[client_id]->get_character(); + return m_apPlayers[ClientId]->GetCharacter(); } -void GAMECONTEXT::create_damageind(vec2 p, float angle, int amount) +void CGameContext::CreateDamageInd(vec2 p, float Angle, int Amount) { - float a = 3 * 3.14159f / 2 + angle; + float a = 3 * 3.14159f / 2 + Angle; //float a = get_angle(dir); float s = a-pi/3; float e = a+pi/3; - for(int i = 0; i < amount; i++) + for(int i = 0; i < Amount; i++) { - float f = mix(s, e, float(i+1)/float(amount+2)); - NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)events.create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); + float f = mix(s, e, float(i+1)/float(Amount+2)); + NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)m_Events.Create(NETEVENTTYPE_DAMAGEIND, sizeof(NETEVENT_DAMAGEIND)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->angle = (int)(f*256.0f); + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; + ev->m_Angle = (int)(f*256.0f); } } } -void GAMECONTEXT::create_hammerhit(vec2 p) +void CGameContext::CreateHammerHit(vec2 p) { // create the event - NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)events.create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); + NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)m_Events.Create(NETEVENTTYPE_HAMMERHIT, sizeof(NETEVENT_HAMMERHIT)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } } -void GAMECONTEXT::create_explosion(vec2 p, int owner, int weapon, bool bnodamage) +void CGameContext::CreateExplosion(vec2 p, int Owner, int Weapon, bool NoDamage) { // create the event - NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)events.create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)m_Events.Create(NETEVENTTYPE_EXPLOSION, sizeof(NETEVENT_EXPLOSION)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } - if (!bnodamage) + if (!NoDamage) { // deal damage - CHARACTER *ents[64]; - float radius = 135.0f; - float innerradius = 48.0f; - int num = game.world.find_entities(p, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER); - for(int i = 0; i < num; i++) + CCharacter *apEnts[64]; + float Radius = 135.0f; + float InnerRadius = 48.0f; + int Num = m_World.FindEntities(p, Radius, (CEntity**)apEnts, 64, NETOBJTYPE_CHARACTER); + for(int i = 0; i < Num; i++) { - vec2 diff = ents[i]->pos - p; - vec2 forcedir(0,1); - float l = length(diff); + vec2 Diff = apEnts[i]->m_Pos - p; + vec2 ForceDir(0,1); + float l = length(Diff); if(l) - forcedir = normalize(diff); - l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); - float dmg = 6 * l; - if((int)dmg) - ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); + ForceDir = normalize(Diff); + l = 1-clamp((l-InnerRadius)/(Radius-InnerRadius), 0.0f, 1.0f); + float Dmg = 6 * l; + if((int)Dmg) + apEnts[i]->TakeDamage(ForceDir*Dmg*2, (int)Dmg, Owner, Weapon); } } } @@ -110,273 +154,893 @@ void create_smoke(vec2 p) } }*/ -void GAMECONTEXT::create_playerspawn(vec2 p) +void CGameContext::CreatePlayerSpawn(vec2 p) { // create the event - NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)events.create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); + NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)m_Events.Create(NETEVENTTYPE_SPAWN, sizeof(NETEVENT_SPAWN)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; } } -void GAMECONTEXT::create_death(vec2 p, int cid) +void CGameContext::CreateDeath(vec2 p, int ClientId) { // create the event - NETEVENT_DEATH *ev = (NETEVENT_DEATH *)events.create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); + NETEVENT_DEATH *ev = (NETEVENT_DEATH *)m_Events.Create(NETEVENTTYPE_DEATH, sizeof(NETEVENT_DEATH)); if(ev) { - ev->x = (int)p.x; - ev->y = (int)p.y; - ev->cid = cid; + ev->m_X = (int)p.x; + ev->m_Y = (int)p.y; + ev->m_ClientId = ClientId; } } -void GAMECONTEXT::create_sound(vec2 pos, int sound, int mask) +void CGameContext::CreateSound(vec2 Pos, int Sound, int Mask) { - if (sound < 0) + if (Sound < 0) return; // create a sound - NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)events.create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), mask); + NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)m_Events.Create(NETEVENTTYPE_SOUNDWORLD, sizeof(NETEVENT_SOUNDWORLD), Mask); if(ev) { - ev->x = (int)pos.x; - ev->y = (int)pos.y; - ev->soundid = sound; + ev->m_X = (int)Pos.x; + ev->m_Y = (int)Pos.y; + ev->m_SoundId = Sound; } } -void GAMECONTEXT::create_sound_global(int sound, int target) +void CGameContext::CreateSoundGlobal(int Sound, int Target) { - if (sound < 0) + if (Sound < 0) return; - NETMSG_SV_SOUNDGLOBAL msg; - msg.soundid = sound; - msg.pack(MSGFLAG_VITAL); - server_send_msg(target); + CNetMsg_Sv_SoundGlobal Msg; + Msg.m_Soundid = Sound; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, Target); } -void GAMECONTEXT::send_chat_target(int to, const char *text) +void CGameContext::SendChatTarget(int To, const char *pText) { - NETMSG_SV_CHAT msg; - msg.team = 0; - msg.cid = -1; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(to); + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 0; + Msg.m_Cid = -1; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, To); } -void GAMECONTEXT::send_chat(int chatter_cid, int team, const char *text) +void CGameContext::SendChat(int ChatterClientId, int Team, const char *pText) { - if(chatter_cid >= 0 && chatter_cid < MAX_CLIENTS) - dbg_msg("chat", "%d:%d:%s: %s", chatter_cid, team, server_clientname(chatter_cid), text); + if(ChatterClientId >= 0 && ChatterClientId < MAX_CLIENTS) + dbg_msg("chat", "%d:%d:%s: %s", ChatterClientId, Team, Server()->ClientName(ChatterClientId), pText); else - dbg_msg("chat", "*** %s", text); + dbg_msg("chat", "*** %s", pText); - if(team == CHAT_ALL) + if(Team == CHAT_ALL) { - NETMSG_SV_CHAT msg; - msg.team = 0; - msg.cid = chatter_cid; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 0; + Msg.m_Cid = ChatterClientId; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } else { - NETMSG_SV_CHAT msg; - msg.team = 1; - msg.cid = chatter_cid; - msg.message = text; + CNetMsg_Sv_Chat Msg; + Msg.m_Team = 1; + Msg.m_Cid = ChatterClientId; + Msg.m_pMessage = pText; // pack one for the recording only - msg.pack(MSGFLAG_VITAL|MSGFLAG_NOSEND); - server_send_msg(-1); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NOSEND, -1); // send to the clients - msg.pack(MSGFLAG_VITAL|MSGFLAG_NORECORD); for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && game.players[i]->team == team) - server_send_msg(i); + if(m_apPlayers[i] && m_apPlayers[i]->GetTeam() == Team) + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_NORECORD, i); } } } -void GAMECONTEXT::send_emoticon(int cid, int emoticon) +void CGameContext::SendEmoticon(int ClientId, int Emoticon) { - NETMSG_SV_EMOTICON msg; - msg.cid = cid; - msg.emoticon = emoticon; - msg.pack(MSGFLAG_VITAL); - server_send_msg(-1); + CNetMsg_Sv_Emoticon Msg; + Msg.m_Cid = ClientId; + Msg.m_Emoticon = Emoticon; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); } -void GAMECONTEXT::send_weapon_pickup(int cid, int weapon) +void CGameContext::SendWeaponPickup(int ClientId, int Weapon) { - NETMSG_SV_WEAPONPICKUP msg; - msg.weapon = weapon; - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + CNetMsg_Sv_WeaponPickup Msg; + Msg.m_Weapon = Weapon; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } -void GAMECONTEXT::send_broadcast(const char *text, int cid) +void CGameContext::SendBroadcast(const char *pText, int ClientId) { - NETMSG_SV_BROADCAST msg; - msg.message = text; - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + CNetMsg_Sv_Broadcast Msg; + Msg.m_pMessage = pText; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } // -void GAMECONTEXT::start_vote(const char *desc, const char *command) +void CGameContext::StartVote(const char *pDesc, const char *pCommand) { // check if a vote is already running - if(vote_closetime) + if(m_VoteCloseTime) return; // reset votes - vote_enforce = VOTE_ENFORCE_UNKNOWN; + m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->vote = 0; + if(m_apPlayers[i]) + { + m_apPlayers[i]->m_Vote = 0; + m_apPlayers[i]->m_VotePos = 0; + } } // start vote - vote_closetime = time_get() + time_freq()*25; - str_copy(vote_description, desc, sizeof(vote_description)); - str_copy(vote_command, command, sizeof(vote_description)); - send_vote_set(-1); - send_vote_status(-1); + m_VoteCloseTime = time_get() + time_freq()*25; + str_copy(m_aVoteDescription, pDesc, sizeof(m_aVoteDescription)); + str_copy(m_aVoteCommand, pCommand, sizeof(m_aVoteCommand)); + SendVoteSet(-1); + m_VoteUpdate = true; } -void GAMECONTEXT::end_vote() +void CGameContext::EndVote() { - vote_closetime = 0; - send_vote_set(-1); + m_VoteCloseTime = 0; + SendVoteSet(-1); } -void GAMECONTEXT::send_vote_set(int cid) +void CGameContext::SendVoteSet(int ClientId) { - NETMSG_SV_VOTE_SET msg; - if(vote_closetime) + CNetMsg_Sv_VoteSet Msg; + if(m_VoteCloseTime) { - msg.timeout = (vote_closetime-time_get())/time_freq(); - msg.description = vote_description; - msg.command = vote_command; + Msg.m_Timeout = (m_VoteCloseTime-time_get())/time_freq(); + Msg.m_pDescription = m_aVoteDescription; + Msg.m_pCommand = ""; } else { - msg.timeout = 0; - msg.description = ""; - msg.command = ""; + Msg.m_Timeout = 0; + Msg.m_pDescription = ""; + Msg.m_pCommand = ""; } - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); } -void GAMECONTEXT::send_vote_status(int cid) +void CGameContext::SendVoteStatus(int ClientId, int Total, int Yes, int No) { - NETMSG_SV_VOTE_STATUS msg = {0}; - for(int i = 0; i < MAX_CLIENTS; i++) + CNetMsg_Sv_VoteStatus Msg = {0}; + Msg.m_Total = Total; + Msg.m_Yes = Yes; + Msg.m_No = No; + Msg.m_Pass = Total - (Yes+No); + + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); + +} + +void CGameContext::AbortVoteKickOnDisconnect(int ClientId) +{ + if(m_VoteCloseTime && !str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientId) + m_VoteCloseTime = -1; +} + + +void CGameContext::CheckPureTuning() +{ + // might not be created yet during start up + if(!m_pController) + return; + + if( str_comp(m_pController->m_pGameType, "DM")==0 || + str_comp(m_pController->m_pGameType, "TDM")==0 || + str_comp(m_pController->m_pGameType, "CTF")==0) { - if(players[i]) + CTuningParams p; + if(mem_comp(&p, &m_Tuning, sizeof(p)) != 0) { - msg.total++; - if(players[i]->vote > 0) - msg.yes++; - else if(players[i]->vote < 0) - msg.no++; - else - msg.pass++; + dbg_msg("server", "resetting tuning due to pure server"); + m_Tuning = p; } } - - msg.pack(MSGFLAG_VITAL); - server_send_msg(cid); - } -void GAMECONTEXT::abort_vote_kick_on_disconnect(int client_id) +void CGameContext::SendTuningParams(int Cid) { - if(vote_closetime && !strncmp(vote_command, "kick ", 5) && atoi(&vote_command[5]) == client_id) - vote_closetime = -1; + CheckPureTuning(); + + CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS); + int *pParams = (int *)&m_Tuning; + for(unsigned i = 0; i < sizeof(m_Tuning)/sizeof(int); i++) + Msg.AddInt(pParams[i]); + Server()->SendMsg(&Msg, MSGFLAG_VITAL, Cid); } -void GAMECONTEXT::tick() +void CGameContext::OnTick() { - world.core.tuning = tuning; - world.tick(); + // check tuning + CheckPureTuning(); + + // copy tuning + m_World.m_Core.m_Tuning = m_Tuning; + m_World.Tick(); //if(world.paused) // make sure that the game object always updates - controller->tick(); + m_pController->Tick(); for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->tick(); + if(m_apPlayers[i]) + m_apPlayers[i]->Tick(); } // update voting - if(vote_closetime) + if(m_VoteCloseTime) { // abort the kick-vote on player-leave - if(vote_closetime == -1) + if(m_VoteCloseTime == -1) { - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote aborted"); - end_vote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote aborted"); + EndVote(); } else { - // count votes - int total = 0, yes = 0, no = 0; - for(int i = 0; i < MAX_CLIENTS; i++) + int Total = 0, Yes = 0, No = 0; + if(m_VoteUpdate) { - if(players[i]) + // count votes + char aaBuf[MAX_CLIENTS][64] = {{0}}; + for(int i = 0; i < MAX_CLIENTS; i++) + if(m_apPlayers[i]) + Server()->GetClientIP(i, aaBuf[i], 64); + bool aVoteChecked[MAX_CLIENTS] = {0}; + for(int i = 0; i < MAX_CLIENTS; i++) { - total++; - if(players[i]->vote > 0) - yes++; - else if(players[i]->vote < 0) - no++; + if(!m_apPlayers[i] || m_apPlayers[i]->GetTeam() == -1 || aVoteChecked[i]) // don't count in votes by spectators + continue; + + int ActVote = m_apPlayers[i]->m_Vote; + int ActVotePos = m_apPlayers[i]->m_VotePos; + + // check for more players with the same ip (only use the vote of the one who voted first) + for(int j = i+1; j < MAX_CLIENTS; ++j) + { + if(!m_apPlayers[j] || aVoteChecked[j] || str_comp(aaBuf[j], aaBuf[i])) + continue; + + aVoteChecked[j] = true; + if(m_apPlayers[j]->m_Vote && (!ActVote || ActVotePos > m_apPlayers[j]->m_VotePos)) + { + ActVote = m_apPlayers[j]->m_Vote; + ActVotePos = m_apPlayers[j]->m_VotePos; + } + } + + Total++; + if(ActVote > 0) + Yes++; + else if(ActVote < 0) + No++; } + + if(Yes >= Total/2+1) + m_VoteEnforce = VOTE_ENFORCE_YES; + else if(No >= Total/2+1 || Yes+No == Total) + m_VoteEnforce = VOTE_ENFORCE_NO; } + + if(m_VoteEnforce == VOTE_ENFORCE_YES) + { + Console()->ExecuteLine(m_aVoteCommand); + EndVote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote passed"); + + if(m_apPlayers[m_VoteCreator]) + m_apPlayers[m_VoteCreator]->m_Last_VoteCall = 0; + } + else if(m_VoteEnforce == VOTE_ENFORCE_NO || time_get() > m_VoteCloseTime) + { + EndVote(); + SendChat(-1, CGameContext::CHAT_ALL, "Vote failed"); + } + else if(m_VoteUpdate) + { + m_VoteUpdate = false; + SendVoteStatus(-1, Total, Yes, No); + } + } + } + + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + for(int i = 0; i < g_Config.m_DbgDummies ; i++) + { + CNetObj_PlayerInput Input = {0}; + Input.m_Direction = (i&1)?-1:1; + m_apPlayers[MAX_CLIENTS-i-1]->OnPredictedInput(&Input); + } + } +#endif +} + +// Server hooks +void CGameContext::OnClientDirectInput(int ClientID, void *pInput) +{ + if(!m_World.m_Paused) + m_apPlayers[ClientID]->OnDirectInput((CNetObj_PlayerInput *)pInput); +} + +void CGameContext::OnClientPredictedInput(int ClientID, void *pInput) +{ + if(!m_World.m_Paused) + m_apPlayers[ClientID]->OnPredictedInput((CNetObj_PlayerInput *)pInput); +} + +void CGameContext::OnClientEnter(int ClientId) +{ + //world.insert_entity(&players[client_id]); + m_apPlayers[ClientId]->Respawn(); + dbg_msg("game", "join player='%d:%s'", ClientId, Server()->ClientName(ClientId)); + + + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "%s entered and joined the %s", Server()->ClientName(ClientId), m_pController->GetTeamName(m_apPlayers[ClientId]->GetTeam())); + SendChat(-1, CGameContext::CHAT_ALL, aBuf); + + dbg_msg("game", "team_join player='%d:%s' team=%d", ClientId, Server()->ClientName(ClientId), m_apPlayers[ClientId]->GetTeam()); + + m_VoteUpdate = true; +} + +void CGameContext::OnClientConnected(int ClientId) +{ + // Check which team the player should be on + const int StartTeam = g_Config.m_SvTournamentMode ? -1 : m_pController->GetAutoTeam(ClientId); + + m_apPlayers[ClientId] = new(ClientId) CPlayer(this, ClientId, StartTeam); + //players[client_id].init(client_id); + //players[client_id].client_id = client_id; + + (void)m_pController->CheckTeamBalance(); + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + if(ClientId >= MAX_CLIENTS-g_Config.m_DbgDummies) + return; + } +#endif + + // send active vote + if(m_VoteCloseTime) + SendVoteSet(ClientId); + + // send motd + CNetMsg_Sv_Motd Msg; + Msg.m_pMessage = g_Config.m_SvMotd; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, ClientId); +} + +void CGameContext::OnClientDrop(int ClientId) +{ + AbortVoteKickOnDisconnect(ClientId); + m_apPlayers[ClientId]->OnDisconnect(); + delete m_apPlayers[ClientId]; + m_apPlayers[ClientId] = 0; + + (void)m_pController->CheckTeamBalance(); + m_VoteUpdate = true; +} + +void CGameContext::OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId) +{ + void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgId, pUnpacker); + CPlayer *p = m_apPlayers[ClientId]; + + if(!pRawMsg) + { + dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler.GetMsgName(MsgId), MsgId, m_NetObjHandler.FailedMsgOn()); + return; + } + + if(MsgId == NETMSGTYPE_CL_SAY) + { + CNetMsg_Cl_Say *pMsg = (CNetMsg_Cl_Say *)pRawMsg; + int Team = pMsg->m_Team; + if(Team) + Team = p->GetTeam(); + else + Team = CGameContext::CHAT_ALL; + + if(g_Config.m_SvSpamprotection && p->m_Last_Chat && p->m_Last_Chat+Server()->TickSpeed() > Server()->Tick()) + return; + + p->m_Last_Chat = Server()->Tick(); + + // check for invalid chars + unsigned char *pMessage = (unsigned char *)pMsg->m_pMessage; + while (*pMessage) + { + if(*pMessage < 32) + *pMessage = ' '; + pMessage++; + } + + SendChat(ClientId, Team, pMsg->m_pMessage); + } + else if(MsgId == NETMSGTYPE_CL_CALLVOTE) + { + if(g_Config.m_SvSpamprotection && p->m_Last_VoteTry && p->m_Last_VoteTry+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + int64 Now = Server()->Tick(); + p->m_Last_VoteTry = Now; + if(m_VoteCloseTime) + { + SendChatTarget(ClientId, "Wait for current vote to end before calling a new one."); + return; + } - if(vote_enforce == VOTE_ENFORCE_YES || yes >= total/2+1) + int Timeleft = p->m_Last_VoteCall + Server()->TickSpeed()*60 - Now; + if(p->m_Last_VoteCall && Timeleft > 0) + { + char aChatmsg[512] = {0}; + str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/Server()->TickSpeed())+1); + SendChatTarget(ClientId, aChatmsg); + return; + } + + char aChatmsg[512] = {0}; + char aDesc[512] = {0}; + char aCmd[512] = {0}; + CNetMsg_Cl_CallVote *pMsg = (CNetMsg_Cl_CallVote *)pRawMsg; + if(str_comp_nocase(pMsg->m_Type, "option") == 0) + { + CVoteOption *pOption = m_pVoteOptionFirst; + while(pOption) { - console_execute_line(vote_command); - end_vote(); - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote passed"); + if(str_comp_nocase(pMsg->m_Value, pOption->m_aCommand) == 0) + { + str_format(aChatmsg, sizeof(aChatmsg), "%s called vote to change server option '%s'", Server()->ClientName(ClientId), pOption->m_aCommand); + str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aCommand); + str_format(aCmd, sizeof(aCmd), "%s", pOption->m_aCommand); + break; + } + + pOption = pOption->m_pNext; + } - if(players[vote_creator]) - players[vote_creator]->last_votecall = 0; + if(!pOption) + { + str_format(aChatmsg, sizeof(aChatmsg), "'%s' isn't an option on this server", pMsg->m_Value); + SendChatTarget(ClientId, aChatmsg); + return; } - else if(vote_enforce == VOTE_ENFORCE_NO || time_get() > vote_closetime || no >= total/2+1 || yes+no == total) + } + else if(str_comp_nocase(pMsg->m_Type, "kick") == 0) + { + if(!g_Config.m_SvVoteKick) { - end_vote(); - send_chat(-1, GAMECONTEXT::CHAT_ALL, "Vote failed"); + SendChatTarget(ClientId, "Server does not allow voting to kick players"); + return; } + + int KickId = str_toint(pMsg->m_Value); + if(KickId < 0 || KickId >= MAX_CLIENTS || !m_apPlayers[KickId]) + { + SendChatTarget(ClientId, "Invalid client id to kick"); + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "%s called for vote to kick '%s'", Server()->ClientName(ClientId), Server()->ClientName(KickId)); + str_format(aDesc, sizeof(aDesc), "Kick '%s'", Server()->ClientName(KickId)); + if (!g_Config.m_SvVoteKickBantime) + str_format(aCmd, sizeof(aCmd), "kick %d", KickId); + else + { + char aBuf[64] = {0}; + Server()->GetClientIP(KickId, aBuf, sizeof(aBuf)); + str_format(aCmd, sizeof(aCmd), "ban %s %d", aBuf, g_Config.m_SvVoteKickBantime); + } + } + + if(aCmd[0]) + { + SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); + StartVote(aDesc, aCmd); + p->m_Vote = 1; + p->m_VotePos = m_VotePos = 1; + m_VoteCreator = ClientId; + p->m_Last_VoteCall = Now; + } + } + else if(MsgId == NETMSGTYPE_CL_VOTE) + { + if(!m_VoteCloseTime) + return; + + if(p->m_Vote == 0) + { + CNetMsg_Cl_Vote *pMsg = (CNetMsg_Cl_Vote *)pRawMsg; + if(!pMsg->m_Vote) + return; + + p->m_Vote = pMsg->m_Vote; + p->m_VotePos = ++m_VotePos; + m_VoteUpdate = true; } } + else if (MsgId == NETMSGTYPE_CL_SETTEAM && !m_World.m_Paused) + { + CNetMsg_Cl_SetTeam *pMsg = (CNetMsg_Cl_SetTeam *)pRawMsg; + + if(p->GetTeam() == pMsg->m_Team || (g_Config.m_SvSpamprotection && p->m_Last_SetTeam && p->m_Last_SetTeam+Server()->TickSpeed()*3 > Server()->Tick())) + return; + + // Switch team on given client and kill/respawn him + if(m_pController->CanJoinTeam(pMsg->m_Team, ClientId)) + { + if(m_pController->CanChangeTeam(p, pMsg->m_Team)) + { + p->m_Last_SetTeam = Server()->Tick(); + if(p->GetTeam() == -1 || pMsg->m_Team == -1) + m_VoteUpdate = true; + p->SetTeam(pMsg->m_Team); + (void)m_pController->CheckTeamBalance(); + } + else + SendBroadcast("Teams must be balanced, please join other team", ClientId); + } + else + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Only %d active players are allowed", g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots); + SendBroadcast(aBuf, ClientId); + } + } + else if (MsgId == NETMSGTYPE_CL_CHANGEINFO || MsgId == NETMSGTYPE_CL_STARTINFO) + { + CNetMsg_Cl_ChangeInfo *pMsg = (CNetMsg_Cl_ChangeInfo *)pRawMsg; + + if(g_Config.m_SvSpamprotection && p->m_Last_ChangeInfo && p->m_Last_ChangeInfo+Server()->TickSpeed()*5 > Server()->Tick()) + return; + + p->m_Last_ChangeInfo = Server()->Tick(); + + p->m_TeeInfos.m_UseCustomColor = pMsg->m_UseCustomColor; + p->m_TeeInfos.m_ColorBody = pMsg->m_ColorBody; + p->m_TeeInfos.m_ColorFeet = pMsg->m_ColorFeet; + + // check for invalid chars + unsigned char *pName = (unsigned char *)pMsg->m_pName; + while (*pName) + { + if(*pName < 32) + *pName = ' '; + pName++; + } + + // copy old name + char aOldName[MAX_NAME_LENGTH]; + str_copy(aOldName, Server()->ClientName(ClientId), MAX_NAME_LENGTH); + + Server()->SetClientName(ClientId, pMsg->m_pName); + if(MsgId == NETMSGTYPE_CL_CHANGEINFO && str_comp(aOldName, Server()->ClientName(ClientId)) != 0) + { + char aChatText[256]; + str_format(aChatText, sizeof(aChatText), "%s changed name to %s", aOldName, Server()->ClientName(ClientId)); + SendChat(-1, CGameContext::CHAT_ALL, aChatText); + } + + // set skin + str_copy(p->m_TeeInfos.m_SkinName, pMsg->m_pSkin, sizeof(p->m_TeeInfos.m_SkinName)); + + m_pController->OnPlayerInfoChange(p); + + if(MsgId == NETMSGTYPE_CL_STARTINFO) + { + // send vote options + CNetMsg_Sv_VoteClearOptions ClearMsg; + Server()->SendPackMsg(&ClearMsg, MSGFLAG_VITAL, ClientId); + CVoteOption *pCurrent = m_pVoteOptionFirst; + while(pCurrent) + { + CNetMsg_Sv_VoteOption OptionMsg; + OptionMsg.m_pCommand = pCurrent->m_aCommand; + Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, ClientId); + pCurrent = pCurrent->m_pNext; + } + + // send tuning parameters to client + SendTuningParams(ClientId); + + // + CNetMsg_Sv_ReadyToEnter m; + Server()->SendPackMsg(&m, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId); + } + } + else if (MsgId == NETMSGTYPE_CL_EMOTICON && !m_World.m_Paused) + { + CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; + + if(g_Config.m_SvSpamprotection && p->m_Last_Emote && p->m_Last_Emote+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + p->m_Last_Emote = Server()->Tick(); + + SendEmoticon(ClientId, pMsg->m_Emoticon); + } + else if (MsgId == NETMSGTYPE_CL_KILL && !m_World.m_Paused) + { + if(p->m_Last_Kill && p->m_Last_Kill+Server()->TickSpeed()*3 > Server()->Tick()) + return; + + p->m_Last_Kill = Server()->Tick(); + p->KillCharacter(WEAPON_SELF); + p->m_RespawnTick = Server()->Tick()+Server()->TickSpeed()*3; + } +} + +void CGameContext::ConTuneParam(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + const char *pParamName = pResult->GetString(0); + float NewValue = pResult->GetFloat(1); + + if(pSelf->Tuning()->Set(pParamName, NewValue)) + { + dbg_msg("tuning", "%s changed to %.2f", pParamName, NewValue); + pSelf->SendTuningParams(-1); + } + else + dbg_msg("tuning", "No such tuning parameter"); +} + +void CGameContext::ConTuneReset(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CTuningParams p; + *pSelf->Tuning() = p; + pSelf->SendTuningParams(-1); + dbg_msg("tuning", "Tuning reset"); +} + +void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + for(int i = 0; i < pSelf->Tuning()->Num(); i++) + { + float v; + pSelf->Tuning()->Get(i, &v); + dbg_msg("tuning", "%s %.2f", pSelf->Tuning()->m_apNames[i], v); + } +} + +void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->m_pController->ChangeMap(pResult->GetString(0)); +} + +void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(pResult->NumArguments()) + pSelf->m_pController->DoWarmup(pResult->GetInteger(0)); + else + pSelf->m_pController->StartRound(); +} + +void CGameContext::ConBroadcast(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SendBroadcast(pResult->GetString(0), -1); +} + +void CGameContext::ConSay(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + pSelf->SendChat(-1, CGameContext::CHAT_ALL, pResult->GetString(0)); +} + +void CGameContext::ConSetTeam(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int ClientId = clamp(pResult->GetInteger(0), 0, (int)MAX_CLIENTS-1); + int Team = clamp(pResult->GetInteger(1), -1, 1); + + dbg_msg("", "%d %d", ClientId, Team); + + if(!pSelf->m_apPlayers[ClientId]) + return; + + pSelf->m_apPlayers[ClientId]->SetTeam(Team); + (void)pSelf->m_pController->CheckTeamBalance(); +} + +void CGameContext::ConAddVote(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + int Len = str_length(pResult->GetString(0)); + + CGameContext::CVoteOption *pOption = (CGameContext::CVoteOption *)pSelf->m_pVoteOptionHeap->Allocate(sizeof(CGameContext::CVoteOption) + Len); + pOption->m_pNext = 0; + pOption->m_pPrev = pSelf->m_pVoteOptionLast; + if(pOption->m_pPrev) + pOption->m_pPrev->m_pNext = pOption; + pSelf->m_pVoteOptionLast = pOption; + if(!pSelf->m_pVoteOptionFirst) + pSelf->m_pVoteOptionFirst = pOption; + + mem_copy(pOption->m_aCommand, pResult->GetString(0), Len+1); + dbg_msg("server", "added option '%s'", pOption->m_aCommand); + + CNetMsg_Sv_VoteOption OptionMsg; + OptionMsg.m_pCommand = pOption->m_aCommand; + pSelf->Server()->SendPackMsg(&OptionMsg, MSGFLAG_VITAL, -1); +} + +void CGameContext::ConVote(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(str_comp_nocase(pResult->GetString(0), "yes") == 0) + pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_YES; + else if(str_comp_nocase(pResult->GetString(0), "no") == 0) + pSelf->m_VoteEnforce = CGameContext::VOTE_ENFORCE_NO; + dbg_msg("server", "forcing vote %s", pResult->GetString(0)); +} + +void CGameContext::ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments()) + { + CNetMsg_Sv_Motd Msg; + Msg.m_pMessage = g_Config.m_SvMotd; + CGameContext *pSelf = (CGameContext *)pUserData; + for(int i = 0; i < MAX_CLIENTS; ++i) + if(pSelf->m_apPlayers[i]) + pSelf->Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, i); + } +} + +void CGameContext::OnConsoleInit() +{ + m_pServer = Kernel()->RequestInterface<IServer>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + + Console()->Register("tune", "si", CFGFLAG_SERVER, ConTuneParam, this, ""); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, ""); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, ""); + + Console()->Register("change_map", "r", CFGFLAG_SERVER, ConChangeMap, this, ""); + Console()->Register("restart", "?i", CFGFLAG_SERVER, ConRestart, this, ""); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, ""); + Console()->Register("say", "r", CFGFLAG_SERVER, ConSay, this, ""); + Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConSetTeam, this, ""); + + Console()->Register("addvote", "r", CFGFLAG_SERVER, ConAddVote, this, ""); + Console()->Register("vote", "r", CFGFLAG_SERVER, ConVote, this, ""); + + Console()->Chain("sv_motd", ConchainSpecialMotdupdate, this); +} + +void CGameContext::OnInit(/*class IKernel *pKernel*/) +{ + m_pServer = Kernel()->RequestInterface<IServer>(); + m_pConsole = Kernel()->RequestInterface<IConsole>(); + m_World.SetGameServer(this); + m_Events.SetGameServer(this); + + //if(!data) // only load once + //data = load_data_from_memory(internal_data); + + for(int i = 0; i < NUM_NETOBJTYPES; i++) + Server()->SnapSetStaticsize(i, m_NetObjHandler.GetObjSize(i)); + + m_Layers.Init(Kernel()); + m_Collision.Init(&m_Layers); + + // reset everything here + //world = new GAMEWORLD; + //players = new CPlayer[MAX_CLIENTS]; + + // select gametype + if(str_comp(g_Config.m_SvGametype, "mod") == 0) + m_pController = new CGameControllerMOD(this); + else if(str_comp(g_Config.m_SvGametype, "ctf") == 0) + m_pController = new CGameControllerCTF(this); + else if(str_comp(g_Config.m_SvGametype, "tdm") == 0) + m_pController = new CGameControllerTDM(this); + else + m_pController = new CGameControllerDM(this); + + // setup core world + //for(int i = 0; i < MAX_CLIENTS; i++) + // game.players[i].core.world = &game.world.core; + + // create all entities from the game layer + CMapItemLayerTilemap *pTileMap = m_Layers.GameLayer(); + CTile *pTiles = (CTile *)Kernel()->RequestInterface<IMap>()->GetData(pTileMap->m_Data); + + + + + /* + num_spawn_points[0] = 0; + num_spawn_points[1] = 0; + num_spawn_points[2] = 0; + */ + + for(int y = 0; y < pTileMap->m_Height; y++) + { + for(int x = 0; x < pTileMap->m_Width; x++) + { + int Index = pTiles[y*pTileMap->m_Width+x].m_Index; + + if(Index >= ENTITY_OFFSET) + { + vec2 Pos(x*32.0f+16.0f, y*32.0f+16.0f); + m_pController->OnEntity(Index-ENTITY_OFFSET, Pos); + } + } + } + + //game.world.insert_entity(game.Controller); + +#ifdef CONF_DEBUG + if(g_Config.m_DbgDummies) + { + for(int i = 0; i < g_Config.m_DbgDummies ; i++) + { + OnClientConnected(MAX_CLIENTS-i-1); + } + } +#endif +} + +void CGameContext::OnShutdown() +{ + delete m_pController; + m_pController = 0; + Clear(); } -void GAMECONTEXT::snap(int client_id) +void CGameContext::OnSnap(int ClientId) { - world.snap(client_id); - controller->snap(client_id); - events.snap(client_id); + m_World.Snap(ClientId); + m_pController->Snap(ClientId); + m_Events.Snap(ClientId); for(int i = 0; i < MAX_CLIENTS; i++) { - if(players[i]) - players[i]->snap(client_id); + if(m_apPlayers[i]) + m_apPlayers[i]->Snap(ClientId); } } +void CGameContext::OnPreSnap() {} +void CGameContext::OnPostSnap() +{ + m_Events.Clear(); +} + +const char *CGameContext::Version() { return GAME_VERSION; } +const char *CGameContext::NetVersion() { return GAME_NETVERSION; } + +IGameServer *CreateGameServer() { return new CGameContext; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h new file mode 100644 index 00000000..d55203e7 --- /dev/null +++ b/src/game/server/gamecontext.h @@ -0,0 +1,168 @@ +#ifndef GAME_SERVER_GAMECONTEXT_H +#define GAME_SERVER_GAMECONTEXT_H + +#include <engine/server.h> +#include <engine/console.h> +#include <engine/shared/memheap.h> + +#include <game/layers.h> + +#include "eventhandler.h" +#include "gamecontroller.h" +#include "gameworld.h" +#include "player.h" + +/* + Tick + Game Context (CGameContext::tick) + Game World (GAMEWORLD::tick) + Reset world if requested (GAMEWORLD::reset) + All entities in the world (ENTITY::tick) + All entities in the world (ENTITY::tick_defered) + Remove entities marked for deletion (GAMEWORLD::remove_entities) + Game Controller (GAMECONTROLLER::tick) + All players (CPlayer::tick) + + + Snap + Game Context (CGameContext::snap) + Game World (GAMEWORLD::snap) + All entities in the world (ENTITY::snap) + Game Controller (GAMECONTROLLER::snap) + Events handler (EVENT_HANDLER::snap) + All players (CPlayer::snap) + +*/ +class CGameContext : public IGameServer +{ + IServer *m_pServer; + class IConsole *m_pConsole; + CLayers m_Layers; + CCollision m_Collision; + CNetObjHandler m_NetObjHandler; + CTuningParams m_Tuning; + + static void ConTuneParam(IConsole::IResult *pResult, void *pUserData); + static void ConTuneReset(IConsole::IResult *pResult, void *pUserData); + static void ConTuneDump(IConsole::IResult *pResult, void *pUserData); + static void ConChangeMap(IConsole::IResult *pResult, void *pUserData); + static void ConRestart(IConsole::IResult *pResult, void *pUserData); + static void ConBroadcast(IConsole::IResult *pResult, void *pUserData); + static void ConSay(IConsole::IResult *pResult, void *pUserData); + static void ConSetTeam(IConsole::IResult *pResult, void *pUserData); + static void ConAddVote(IConsole::IResult *pResult, void *pUserData); + static void ConVote(IConsole::IResult *pResult, void *pUserData); + static void ConchainSpecialMotdupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); + + CGameContext(int Resetting); + void Construct(int Resetting); + + bool m_Resetting; +public: + IServer *Server() const { return m_pServer; } + class IConsole *Console() { return m_pConsole; } + CCollision *Collision() { return &m_Collision; } + CTuningParams *Tuning() { return &m_Tuning; } + + CGameContext(); + ~CGameContext(); + + void Clear(); + + CEventHandler m_Events; + CPlayer *m_apPlayers[MAX_CLIENTS]; + + IGameController *m_pController; + CGameWorld m_World; + + // helper functions + class CCharacter *GetPlayerChar(int ClientId); + + // voting + void StartVote(const char *pDesc, const char *pCommand); + void EndVote(); + void SendVoteSet(int ClientId); + void SendVoteStatus(int ClientId, int Total, int Yes, int No); + void AbortVoteKickOnDisconnect(int ClientId); + + int m_VoteCreator; + int64 m_VoteCloseTime; + bool m_VoteUpdate; + int m_VotePos; + char m_aVoteDescription[512]; + char m_aVoteCommand[512]; + int m_VoteEnforce; + enum + { + VOTE_ENFORCE_UNKNOWN=0, + VOTE_ENFORCE_NO, + VOTE_ENFORCE_YES, + }; + struct CVoteOption + { + CVoteOption *m_pNext; + CVoteOption *m_pPrev; + char m_aCommand[1]; + }; + CHeap *m_pVoteOptionHeap; + CVoteOption *m_pVoteOptionFirst; + CVoteOption *m_pVoteOptionLast; + + // helper functions + void CreateDamageInd(vec2 Pos, float AngleMod, int Amount); + void CreateExplosion(vec2 Pos, int Owner, int Weapon, bool NoDamage); + void CreateSmoke(vec2 Pos); + void CreateHammerHit(vec2 Pos); + void CreatePlayerSpawn(vec2 Pos); + void CreateDeath(vec2 Pos, int Who); + void CreateSound(vec2 Pos, int Sound, int Mask=-1); + void CreateSoundGlobal(int Sound, int Target=-1); + + + enum + { + CHAT_ALL=-2, + CHAT_SPEC=-1, + CHAT_RED=0, + CHAT_BLUE=1 + }; + + // network + void SendChatTarget(int To, const char *pText); + void SendChat(int ClientId, int Team, const char *pText); + void SendEmoticon(int ClientId, int Emoticon); + void SendWeaponPickup(int ClientId, int Weapon); + void SendBroadcast(const char *pText, int ClientId); + + + // + void CheckPureTuning(); + void SendTuningParams(int ClientId); + + // engine events + virtual void OnInit(); + virtual void OnConsoleInit(); + virtual void OnShutdown(); + + virtual void OnTick(); + virtual void OnPreSnap(); + virtual void OnSnap(int ClientId); + virtual void OnPostSnap(); + + virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, int ClientId); + + virtual void OnClientConnected(int ClientId); + virtual void OnClientEnter(int ClientId); + virtual void OnClientDrop(int ClientId); + virtual void OnClientDirectInput(int ClientId, void *pInput); + virtual void OnClientPredictedInput(int ClientId, void *pInput); + + virtual const char *Version(); + virtual const char *NetVersion(); +}; + +inline int CmaskAll() { return -1; } +inline int CmaskOne(int ClientId) { return 1<<ClientId; } +inline int CmaskAllExceptOne(int ClientId) { return 0x7fffffff^CmaskOne(ClientId); } +inline bool CmaskIsSet(int Mask, int ClientId) { return (Mask&CmaskOne(ClientId)) != 0; } +#endif diff --git a/src/game/server/gamecontext.hpp b/src/game/server/gamecontext.hpp deleted file mode 100644 index bea087cb..00000000 --- a/src/game/server/gamecontext.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef GAME_SERVER_GAMECONTEXT_H -#define GAME_SERVER_GAMECONTEXT_H - -#include "eventhandler.hpp" -#include "gamecontroller.hpp" -#include "gameworld.hpp" -#include "player.hpp" - -/* - Tick - Game Context (GAMECONTEXT::tick) - Game World (GAMEWORLD::tick) - Reset world if requested (GAMEWORLD::reset) - All entities in the world (ENTITY::tick) - All entities in the world (ENTITY::tick_defered) - Remove entities marked for deletion (GAMEWORLD::remove_entities) - Game Controller (GAMECONTROLLER::tick) - All players (PLAYER::tick) - - - Snap - Game Context (GAMECONTEXT::snap) - Game World (GAMEWORLD::snap) - All entities in the world (ENTITY::snap) - Game Controller (GAMECONTROLLER::snap) - Events handler (EVENT_HANDLER::snap) - All players (PLAYER::snap) - -*/ -class GAMECONTEXT -{ -public: - GAMECONTEXT(); - ~GAMECONTEXT(); - - void clear(); - - EVENTHANDLER events; - PLAYER *players[MAX_CLIENTS]; - - GAMECONTROLLER *controller; - GAMEWORLD world; - - void tick(); - void snap(int client_id); - - // helper functions - class CHARACTER *get_player_char(int client_id); - - // voting - void start_vote(const char *desc, const char *command); - void end_vote(); - void send_vote_set(int cid); - void send_vote_status(int cid); - void abort_vote_kick_on_disconnect(int client_id); - int vote_creator; - int64 vote_closetime; - char vote_description[512]; - char vote_command[512]; - int vote_enforce; - enum - { - VOTE_ENFORCE_UNKNOWN=0, - VOTE_ENFORCE_NO, - VOTE_ENFORCE_YES, - }; - - // helper functions - void create_damageind(vec2 p, float angle_mod, int amount); - void create_explosion(vec2 p, int owner, int weapon, bool bnodamage); - void create_smoke(vec2 p); - void create_hammerhit(vec2 p); - void create_playerspawn(vec2 p); - void create_death(vec2 p, int who); - void create_sound(vec2 pos, int sound, int mask=-1); - void create_sound_global(int sound, int target=-1); - - - enum - { - CHAT_ALL=-2, - CHAT_SPEC=-1, - CHAT_RED=0, - CHAT_BLUE=1 - }; - - // network - void send_chat_target(int to, const char *text); - void send_chat(int cid, int team, const char *text); - void send_emoticon(int cid, int emoticon); - void send_weapon_pickup(int cid, int weapon); - void send_broadcast(const char *text, int cid); - -}; - -extern GAMECONTEXT game; - -// MISC stuff, move to a better place later on - -extern TUNING_PARAMS tuning; -inline int cmask_all() { return -1; } -inline int cmask_one(int cid) { return 1<<cid; } -inline int cmask_all_except_one(int cid) { return 0x7fffffff^cmask_one(cid); } -inline bool cmask_is_set(int mask, int cid) { return (mask&cmask_one(cid)) != 0; } -#endif diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp index cdbace8e..a822f7ca 100644 --- a/src/game/server/gamecontroller.cpp +++ b/src/game/server/gamecontroller.cpp @@ -1,607 +1,614 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <string.h> -#include <engine/e_config.h> -#include <engine/e_server_interface.h> -#include <game/mapitems.hpp> +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <engine/shared/config.h> +#include <game/mapitems.h> -#include <game/generated/g_protocol.hpp> +#include <game/generated/protocol.h> -#include "entities/pickup.hpp" -#include "gamecontroller.hpp" -#include "gamecontext.hpp" +#include "entities/pickup.h" +#include "gamecontroller.h" +#include "gamecontext.h" - -GAMECONTROLLER::GAMECONTROLLER() +IGameController::IGameController(class CGameContext *pGameServer) { - gametype = "unknown"; + m_pGameServer = pGameServer; + m_pServer = m_pGameServer->Server(); + m_pGameType = "unknown"; // - do_warmup(config.sv_warmup); - game_over_tick = -1; - sudden_death = 0; - round_start_tick = server_tick(); - round_count = 0; - game_flags = 0; - teamscore[0] = 0; - teamscore[1] = 0; - map_wish[0] = 0; - - unbalanced_tick = -1; - force_balanced = false; - - num_spawn_points[0] = 0; - num_spawn_points[1] = 0; - num_spawn_points[2] = 0; + DoWarmup(g_Config.m_SvWarmup); + m_GameOverTick = -1; + m_SuddenDeath = 0; + m_RoundStartTick = Server()->Tick(); + m_RoundCount = 0; + m_GameFlags = 0; + m_aTeamscore[0] = 0; + m_aTeamscore[1] = 0; + m_aMapWish[0] = 0; + + m_UnbalancedTick = -1; + m_ForceBalanced = false; + + m_aNumSpawnPoints[0] = 0; + m_aNumSpawnPoints[1] = 0; + m_aNumSpawnPoints[2] = 0; } -GAMECONTROLLER::~GAMECONTROLLER() +IGameController::~IGameController() { } -float GAMECONTROLLER::evaluate_spawn_pos(SPAWNEVAL *eval, vec2 pos) +float IGameController::EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos) { - float score = 0.0f; - CHARACTER *c = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; c; c = (CHARACTER *)c->typenext()) + float Score = 0.0f; + CCharacter *pC = static_cast<CCharacter *>(GameServer()->m_World.FindFirst(NETOBJTYPE_CHARACTER)); + for(; pC; pC = (CCharacter *)pC->TypeNext()) { // team mates are not as dangerous as enemies - float scoremod = 1.0f; - if(eval->friendly_team != -1 && c->team == eval->friendly_team) - scoremod = 0.5f; + float Scoremod = 1.0f; + if(pEval->m_FriendlyTeam != -1 && pC->GetPlayer()->GetTeam() == pEval->m_FriendlyTeam) + Scoremod = 0.5f; - float d = distance(pos, c->pos); + float d = distance(Pos, pC->m_Pos); if(d == 0) - score += 1000000000.0f; + Score += 1000000000.0f; else - score += 1.0f/d; + Score += 1.0f/d; } - return score; + return Score; } -void GAMECONTROLLER::evaluate_spawn_type(SPAWNEVAL *eval, int t) +void IGameController::EvaluateSpawnType(CSpawnEval *pEval, int T) { // get spawn point - for(int i = 0; i < num_spawn_points[t]; i++) + for(int i = 0; i < m_aNumSpawnPoints[T]; i++) { - vec2 p = spawn_points[t][i]; - float s = evaluate_spawn_pos(eval, p); - if(!eval->got || eval->score > s) + vec2 P = m_aaSpawnPoints[T][i]; + float S = EvaluateSpawnPos(pEval, P); + if(!pEval->m_Got || pEval->m_Score > S) { - eval->got = true; - eval->score = s; - eval->pos = p; + pEval->m_Got = true; + pEval->m_Score = S; + pEval->m_Pos = P; } } } -bool GAMECONTROLLER::can_spawn(PLAYER *player, vec2 *out_pos) +bool IGameController::CanSpawn(CPlayer *pPlayer, vec2 *pOutPos) { - SPAWNEVAL eval; + CSpawnEval Eval; // spectators can't spawn - if(player->team == -1) + if(pPlayer->GetTeam() == -1) return false; - if(is_teamplay()) + if(IsTeamplay()) { - eval.friendly_team = player->team; + Eval.m_FriendlyTeam = pPlayer->GetTeam(); // try first try own team spawn, then normal spawn and then enemy - evaluate_spawn_type(&eval, 1+(player->team&1)); - if(!eval.got) + EvaluateSpawnType(&Eval, 1+(pPlayer->GetTeam()&1)); + if(!Eval.m_Got) { - evaluate_spawn_type(&eval, 0); - if(!eval.got) - evaluate_spawn_type(&eval, 1+((player->team+1)&1)); + EvaluateSpawnType(&Eval, 0); + if(!Eval.m_Got) + EvaluateSpawnType(&Eval, 1+((pPlayer->GetTeam()+1)&1)); } } else { - evaluate_spawn_type(&eval, 0); - evaluate_spawn_type(&eval, 1); - evaluate_spawn_type(&eval, 2); + EvaluateSpawnType(&Eval, 0); + EvaluateSpawnType(&Eval, 1); + EvaluateSpawnType(&Eval, 2); } - *out_pos = eval.pos; - return eval.got; + *pOutPos = Eval.m_Pos; + return Eval.m_Got; } -bool GAMECONTROLLER::on_entity(int index, vec2 pos) +bool IGameController::OnEntity(int Index, vec2 Pos) { - int type = -1; - int subtype = 0; + int Type = -1; + int SubType = 0; - if(index == ENTITY_SPAWN) - spawn_points[0][num_spawn_points[0]++] = pos; - else if(index == ENTITY_SPAWN_RED) - spawn_points[1][num_spawn_points[1]++] = pos; - else if(index == ENTITY_SPAWN_BLUE) - spawn_points[2][num_spawn_points[2]++] = pos; - else if(index == ENTITY_ARMOR_1) - type = POWERUP_ARMOR; - else if(index == ENTITY_HEALTH_1) - type = POWERUP_HEALTH; - else if(index == ENTITY_WEAPON_SHOTGUN) + if(Index == ENTITY_SPAWN) + m_aaSpawnPoints[0][m_aNumSpawnPoints[0]++] = Pos; + else if(Index == ENTITY_SPAWN_RED) + m_aaSpawnPoints[1][m_aNumSpawnPoints[1]++] = Pos; + else if(Index == ENTITY_SPAWN_BLUE) + m_aaSpawnPoints[2][m_aNumSpawnPoints[2]++] = Pos; + else if(Index == ENTITY_ARMOR_1) + Type = POWERUP_ARMOR; + else if(Index == ENTITY_HEALTH_1) + Type = POWERUP_HEALTH; + else if(Index == ENTITY_WEAPON_SHOTGUN) { - type = POWERUP_WEAPON; - subtype = WEAPON_SHOTGUN; + Type = POWERUP_WEAPON; + SubType = WEAPON_SHOTGUN; } - else if(index == ENTITY_WEAPON_GRENADE) + else if(Index == ENTITY_WEAPON_GRENADE) { - type = POWERUP_WEAPON; - subtype = WEAPON_GRENADE; + Type = POWERUP_WEAPON; + SubType = WEAPON_GRENADE; } - else if(index == ENTITY_WEAPON_RIFLE) + else if(Index == ENTITY_WEAPON_RIFLE) { - type = POWERUP_WEAPON; - subtype = WEAPON_RIFLE; + Type = POWERUP_WEAPON; + SubType = WEAPON_RIFLE; } - else if(index == ENTITY_POWERUP_NINJA && config.sv_powerups) + else if(Index == ENTITY_POWERUP_NINJA && g_Config.m_SvPowerups) { - type = POWERUP_NINJA; - subtype = WEAPON_NINJA; + Type = POWERUP_NINJA; + SubType = WEAPON_NINJA; } - if(type != -1) + if(Type != -1) { - PICKUP *pickup = new PICKUP(type, subtype); - pickup->pos = pos; + CPickup *pPickup = new CPickup(&GameServer()->m_World, Type, SubType); + pPickup->m_Pos = Pos; return true; } return false; } -void GAMECONTROLLER::endround() +void IGameController::EndRound() { - if(warmup) // game can't end when we are running warmup + if(m_Warmup) // game can't end when we are running warmup return; - game.world.paused = true; - game_over_tick = server_tick(); - sudden_death = 0; + GameServer()->m_World.m_Paused = true; + m_GameOverTick = Server()->Tick(); + m_SuddenDeath = 0; } -void GAMECONTROLLER::resetgame() +void IGameController::ResetGame() { - game.world.reset_requested = true; + GameServer()->m_World.m_ResetRequested = true; } -const char *GAMECONTROLLER::get_team_name(int team) +const char *IGameController::GetTeamName(int Team) { - if(is_teamplay()) + if(IsTeamplay()) { - if(team == 0) + if(Team == 0) return "red team"; - else if(team == 1) + else if(Team == 1) return "blue team"; } else { - if(team == 0) + if(Team == 0) return "game"; } return "spectators"; } -static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } +static bool IsSeparator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } -void GAMECONTROLLER::startround() +void IGameController::StartRound() { - resetgame(); - - round_start_tick = server_tick(); - sudden_death = 0; - game_over_tick = -1; - game.world.paused = false; - teamscore[0] = 0; - teamscore[1] = 0; - unbalanced_tick = -1; - force_balanced = false; - dbg_msg("game","start round type='%s' teamplay='%d'", gametype, game_flags&GAMEFLAG_TEAMS); + ResetGame(); + + m_RoundStartTick = Server()->Tick(); + m_SuddenDeath = 0; + m_GameOverTick = -1; + GameServer()->m_World.m_Paused = false; + m_aTeamscore[0] = 0; + m_aTeamscore[1] = 0; + m_ForceBalanced = false; + dbg_msg("game","start round type='%s' teamplay='%d'", m_pGameType, m_GameFlags&GAMEFLAG_TEAMS); } -void GAMECONTROLLER::change_map(const char *to_map) +void IGameController::ChangeMap(const char *pToMap) { - str_copy(map_wish, to_map, sizeof(map_wish)); - endround(); + str_copy(m_aMapWish, pToMap, sizeof(m_aMapWish)); + EndRound(); } -void GAMECONTROLLER::cyclemap() +void IGameController::CycleMap() { - if(map_wish[0] != 0) + if(m_aMapWish[0] != 0) { - dbg_msg("game", "rotating map to %s", map_wish); - str_copy(config.sv_map, map_wish, sizeof(config.sv_map)); - map_wish[0] = 0; - round_count = 0; + dbg_msg("game", "rotating map to %s", m_aMapWish); + str_copy(g_Config.m_SvMap, m_aMapWish, sizeof(g_Config.m_SvMap)); + m_aMapWish[0] = 0; + m_RoundCount = 0; return; } - if(!strlen(config.sv_maprotation)) + if(!str_length(g_Config.m_SvMaprotation)) return; - if(round_count < config.sv_rounds_per_map-1) + if(m_RoundCount < g_Config.m_SvRoundsPerMap-1) return; // handle maprotation - const char *map_rotation = config.sv_maprotation; - const char *current_map = config.sv_map; + const char *pMapRotation = g_Config.m_SvMaprotation; + const char *pCurrentMap = g_Config.m_SvMap; - int current_map_len = strlen(current_map); - const char *next_map = map_rotation; - while(*next_map) + int CurrentMapLen = str_length(pCurrentMap); + const char *pNextMap = pMapRotation; + while(*pNextMap) { - int wordlen = 0; - while(next_map[wordlen] && !is_separator(next_map[wordlen])) - wordlen++; + int WordLen = 0; + while(pNextMap[WordLen] && !IsSeparator(pNextMap[WordLen])) + WordLen++; - if(wordlen == current_map_len && strncmp(next_map, current_map, current_map_len) == 0) + if(WordLen == CurrentMapLen && str_comp_num(pNextMap, pCurrentMap, CurrentMapLen) == 0) { // map found - next_map += current_map_len; - while(*next_map && is_separator(*next_map)) - next_map++; + pNextMap += CurrentMapLen; + while(*pNextMap && IsSeparator(*pNextMap)) + pNextMap++; break; } - next_map++; + pNextMap++; } // restart rotation - if(next_map[0] == 0) - next_map = map_rotation; + if(pNextMap[0] == 0) + pNextMap = pMapRotation; // cut out the next map - char buf[512]; + char Buf[512]; for(int i = 0; i < 512; i++) { - buf[i] = next_map[i]; - if(is_separator(next_map[i]) || next_map[i] == 0) + Buf[i] = pNextMap[i]; + if(IsSeparator(pNextMap[i]) || pNextMap[i] == 0) { - buf[i] = 0; + Buf[i] = 0; break; } } // skip spaces int i = 0; - while(is_separator(buf[i])) + while(IsSeparator(Buf[i])) i++; - round_count = 0; + m_RoundCount = 0; - dbg_msg("game", "rotating map to %s", &buf[i]); - str_copy(config.sv_map, &buf[i], sizeof(config.sv_map)); + dbg_msg("game", "rotating map to %s", &Buf[i]); + str_copy(g_Config.m_SvMap, &Buf[i], sizeof(g_Config.m_SvMap)); } -void GAMECONTROLLER::post_reset() +void IGameController::PostReset() { for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) + if(GameServer()->m_apPlayers[i]) { - game.players[i]->respawn(); - game.players[i]->score = 0; + GameServer()->m_apPlayers[i]->Respawn(); + GameServer()->m_apPlayers[i]->m_Score = 0; } } } -void GAMECONTROLLER::on_player_info_change(class PLAYER *p) +void IGameController::OnPlayerInfoChange(class CPlayer *pP) { - const int team_colors[2] = {65387, 10223467}; - if(is_teamplay()) + const int aTeamColors[2] = {65387, 10223467}; + if(IsTeamplay()) { - if(p->team >= 0 || p->team <= 1) + if(pP->GetTeam() >= 0 || pP->GetTeam() <= 1) { - p->use_custom_color = 1; - p->color_body = team_colors[p->team]; - p->color_feet = team_colors[p->team]; + pP->m_TeeInfos.m_UseCustomColor = 1; + pP->m_TeeInfos.m_ColorBody = aTeamColors[pP->GetTeam()]; + pP->m_TeeInfos.m_ColorFeet = aTeamColors[pP->GetTeam()]; } } } -int GAMECONTROLLER::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) +int IGameController::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) { // do scoreing - if(!killer) + if(!pKiller) return 0; - if(killer == victim->player) - victim->player->score--; // suicide + if(pKiller == pVictim->GetPlayer()) + pVictim->GetPlayer()->m_Score--; // suicide else { - if(is_teamplay() && victim->team == killer->team) - killer->score--; // teamkill + if(IsTeamplay() && pVictim->GetPlayer()->GetTeam() == pKiller->GetTeam()) + pKiller->m_Score--; // teamkill else - killer->score++; // normal kill + pKiller->m_Score++; // normal kill } return 0; } -void GAMECONTROLLER::on_character_spawn(class CHARACTER *chr) +void IGameController::OnCharacterSpawn(class CCharacter *pChr) { // default health - chr->health = 10; + pChr->IncreaseHealth(10); // give default weapons - chr->weapons[WEAPON_HAMMER].got = 1; - chr->weapons[WEAPON_HAMMER].ammo = -1; - chr->weapons[WEAPON_GUN].got = 1; - chr->weapons[WEAPON_GUN].ammo = 10; + pChr->GiveWeapon(WEAPON_HAMMER, -1); + pChr->GiveWeapon(WEAPON_GUN, 10); } -void GAMECONTROLLER::do_warmup(int seconds) +void IGameController::DoWarmup(int Seconds) { - warmup = seconds*server_tickspeed(); + if(Seconds < 0) + m_Warmup = 0; + else + m_Warmup = Seconds*Server()->TickSpeed(); } -bool GAMECONTROLLER::is_friendly_fire(int cid1, int cid2) +bool IGameController::IsFriendlyFire(int Cid1, int Cid2) { - if(cid1 == cid2) + if(Cid1 == Cid2) return false; - if(is_teamplay()) + if(IsTeamplay()) { - if(!game.players[cid1] || !game.players[cid2]) + if(!GameServer()->m_apPlayers[Cid1] || !GameServer()->m_apPlayers[Cid2]) return false; - if(game.players[cid1]->team == game.players[cid2]->team) + if(GameServer()->m_apPlayers[Cid1]->GetTeam() == GameServer()->m_apPlayers[Cid2]->GetTeam()) return true; } return false; } -bool GAMECONTROLLER::is_force_balanced() +bool IGameController::IsForceBalanced() { - if(force_balanced) + if(m_ForceBalanced) { - force_balanced = false; + m_ForceBalanced = false; return true; } else return false; } -void GAMECONTROLLER::tick() +bool IGameController::CanBeMovedOnBalance(int Cid) +{ + return true; +} + +void IGameController::Tick() { // do warmup - if(warmup) + if(m_Warmup) { - warmup--; - if(!warmup) - startround(); + m_Warmup--; + if(!m_Warmup) + StartRound(); } - if(game_over_tick != -1) + if(m_GameOverTick != -1) { // game over.. wait for restart - if(server_tick() > game_over_tick+server_tickspeed()*10) + if(Server()->Tick() > m_GameOverTick+Server()->TickSpeed()*10) { - cyclemap(); - startround(); - round_count++; + CycleMap(); + StartRound(); + m_RoundCount++; } } // do team-balancing - if (is_teamplay() && unbalanced_tick != -1 && server_tick() > unbalanced_tick+config.sv_teambalance_time*server_tickspeed()*60) + if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60) { dbg_msg("game", "Balancing teams"); - int t[2] = {0,0}; - int tscore[2] = {0,0}; + int aT[2] = {0,0}; + int aTScore[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && game.players[i]->team != -1) + if(GameServer()->m_apPlayers[i] && GameServer()->m_apPlayers[i]->GetTeam() != -1) { - t[game.players[i]->team]++; - tscore[game.players[i]->team]+=game.players[i]->score; + aT[GameServer()->m_apPlayers[i]->GetTeam()]++; + aTScore[GameServer()->m_apPlayers[i]->GetTeam()] += GameServer()->m_apPlayers[i]->m_Score; } } // are teams unbalanced? - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { - int m = (t[0] > t[1]) ? 0 : 1; - int num_balance = abs(t[0]-t[1]) / 2; + int M = (aT[0] > aT[1]) ? 0 : 1; + int NumBalance = absolute(aT[0]-aT[1]) / 2; do { - PLAYER *p = 0; - int pd = tscore[m]; + CPlayer *pP = 0; + int PD = aTScore[M]; for(int i = 0; i < MAX_CLIENTS; i++) { - if(!game.players[i]) + if(!GameServer()->m_apPlayers[i]) + continue; + if(!CanBeMovedOnBalance(i)) continue; - // remember the player who would cause lowest score-difference - if(game.players[i]->team == m && (!p || abs((tscore[m^1]+game.players[i]->score) - (tscore[m]-game.players[i]->score)) < pd)) + if(GameServer()->m_apPlayers[i]->GetTeam() == M && (!pP || absolute((aTScore[M^1]+GameServer()->m_apPlayers[i]->m_Score) - (aTScore[M]-GameServer()->m_apPlayers[i]->m_Score)) < PD)) { - p = game.players[i]; - pd = abs((tscore[m^1]+p->score) - (tscore[m]-p->score)); + pP = GameServer()->m_apPlayers[i]; + PD = absolute((aTScore[M^1]+pP->m_Score) - (aTScore[M]-pP->m_Score)); } } // move the player to other team without losing his score // TODO: change in player::set_team needed: player won't lose score on team-change - int score_before = p->score; - p->set_team(m^1); - p->score = score_before; + int ScoreBefore = pP->m_Score; + pP->SetTeam(M^1); + pP->m_Score = ScoreBefore; - p->respawn(); - p->force_balanced = true; - } while (--num_balance); + pP->Respawn(); + pP->m_ForceBalanced = true; + } while (--NumBalance); - force_balanced = true; + m_ForceBalanced = true; } - unbalanced_tick = -1; + m_UnbalancedTick = -1; } // update browse info - int prog = -1; - if(config.sv_timelimit > 0) - prog = max(prog, (server_tick()-round_start_tick) * 100 / (config.sv_timelimit*server_tickspeed()*60)); + int Prog = -1; + if(g_Config.m_SvTimelimit > 0) + Prog = max(Prog, (Server()->Tick()-m_RoundStartTick) * 100 / (g_Config.m_SvTimelimit*Server()->TickSpeed()*60)); - if(config.sv_scorelimit) + if(g_Config.m_SvScorelimit) { - if(is_teamplay()) + if(IsTeamplay()) { - prog = max(prog, (teamscore[0]*100)/config.sv_scorelimit); - prog = max(prog, (teamscore[1]*100)/config.sv_scorelimit); + Prog = max(Prog, (m_aTeamscore[0]*100)/g_Config.m_SvScorelimit); + Prog = max(Prog, (m_aTeamscore[1]*100)/g_Config.m_SvScorelimit); } else { for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) - prog = max(prog, (game.players[i]->score*100)/config.sv_scorelimit); + if(GameServer()->m_apPlayers[i]) + Prog = max(Prog, (GameServer()->m_apPlayers[i]->m_Score*100)/g_Config.m_SvScorelimit); } } } - if(warmup) - prog = -1; + if(m_Warmup) + Prog = -1; - server_setbrowseinfo(gametype, prog); + Server()->SetBrowseInfo(m_pGameType, Prog); } -bool GAMECONTROLLER::is_teamplay() const +bool IGameController::IsTeamplay() const { - return game_flags&GAMEFLAG_TEAMS; + return m_GameFlags&GAMEFLAG_TEAMS; } -void GAMECONTROLLER::snap(int snapping_client) +void IGameController::Snap(int SnappingClient) { - NETOBJ_GAME *gameobj = (NETOBJ_GAME *)snap_new_item(NETOBJTYPE_GAME, 0, sizeof(NETOBJ_GAME)); - gameobj->paused = game.world.paused; - gameobj->game_over = game_over_tick==-1?0:1; - gameobj->sudden_death = sudden_death; + CNetObj_Game *pGameObj = (CNetObj_Game *)Server()->SnapNewItem(NETOBJTYPE_GAME, 0, sizeof(CNetObj_Game)); + pGameObj->m_Paused = GameServer()->m_World.m_Paused; + pGameObj->m_GameOver = m_GameOverTick==-1?0:1; + pGameObj->m_SuddenDeath = m_SuddenDeath; - gameobj->score_limit = config.sv_scorelimit; - gameobj->time_limit = config.sv_timelimit; - gameobj->round_start_tick = round_start_tick; - gameobj->flags = game_flags; + pGameObj->m_ScoreLimit = g_Config.m_SvScorelimit; + pGameObj->m_TimeLimit = g_Config.m_SvTimelimit; + pGameObj->m_RoundStartTick = m_RoundStartTick; + pGameObj->m_Flags = m_GameFlags; - gameobj->warmup = warmup; + pGameObj->m_Warmup = m_Warmup; - gameobj->round_num = (strlen(config.sv_maprotation) && config.sv_rounds_per_map) ? config.sv_rounds_per_map : 0; - gameobj->round_current = round_count+1; + pGameObj->m_RoundNum = (str_length(g_Config.m_SvMaprotation) && g_Config.m_SvRoundsPerMap) ? g_Config.m_SvRoundsPerMap : 0; + pGameObj->m_RoundCurrent = m_RoundCount+1; - if(snapping_client == -1) + if(SnappingClient == -1) { // we are recording a demo, just set the scores - gameobj->teamscore_red = teamscore[0]; - gameobj->teamscore_blue = teamscore[1]; + pGameObj->m_TeamscoreRed = m_aTeamscore[0]; + pGameObj->m_TeamscoreBlue = m_aTeamscore[1]; } else { // TODO: this little hack should be removed - gameobj->teamscore_red = is_teamplay() ? teamscore[0] : game.players[snapping_client]->score; - gameobj->teamscore_blue = teamscore[1]; + pGameObj->m_TeamscoreRed = IsTeamplay() ? m_aTeamscore[0] : GameServer()->m_apPlayers[SnappingClient]->m_Score; + pGameObj->m_TeamscoreBlue = m_aTeamscore[1]; } } -int GAMECONTROLLER::get_auto_team(int notthisid) +int IGameController::GetAutoTeam(int Notthisid) { // this will force the auto balancer to work overtime aswell - if(config.dbg_stress) + if(g_Config.m_DbgStress) return 0; - int numplayers[2] = {0,0}; + int aNumplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && i != notthisid) + if(GameServer()->m_apPlayers[i] && i != Notthisid) { - if(game.players[i]->team == 0 || game.players[i]->team == 1) - numplayers[game.players[i]->team]++; + if(GameServer()->m_apPlayers[i]->GetTeam() == 0 || GameServer()->m_apPlayers[i]->GetTeam() == 1) + aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; } } - int team = 0; - if(is_teamplay()) - team = numplayers[0] > numplayers[1] ? 1 : 0; + int Team = 0; + if(IsTeamplay()) + Team = aNumplayers[0] > aNumplayers[1] ? 1 : 0; - if(can_join_team(team, notthisid)) - return team; + if(CanJoinTeam(Team, Notthisid)) + return Team; return -1; } -bool GAMECONTROLLER::can_join_team(int team, int notthisid) +bool IGameController::CanJoinTeam(int Team, int Notthisid) { - (void)team; - int numplayers[2] = {0,0}; + if(Team == -1) + return true; + + int aNumplayers[2] = {0,0}; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i] && i != notthisid) + if(GameServer()->m_apPlayers[i] && i != Notthisid) { - if(game.players[i]->team >= 0 || game.players[i]->team == 1) - numplayers[game.players[i]->team]++; + if(GameServer()->m_apPlayers[i]->GetTeam() >= 0 || GameServer()->m_apPlayers[i]->GetTeam() == 1) + aNumplayers[GameServer()->m_apPlayers[i]->GetTeam()]++; } } - return (numplayers[0] + numplayers[1]) < config.sv_max_clients-config.sv_spectator_slots; + return (aNumplayers[0] + aNumplayers[1]) < g_Config.m_SvMaxClients-g_Config.m_SvSpectatorSlots; } -bool GAMECONTROLLER::check_team_balance() +bool IGameController::CheckTeamBalance() { - if(!is_teamplay() || !config.sv_teambalance_time) + if(!IsTeamplay() || !g_Config.m_SvTeambalanceTime) return true; - int t[2] = {0, 0}; + int aT[2] = {0, 0}; for(int i = 0; i < MAX_CLIENTS; i++) { - PLAYER *p = game.players[i]; - if(p && p->team != -1) - t[p->team]++; + CPlayer *pP = GameServer()->m_apPlayers[i]; + if(pP && pP->GetTeam() != -1) + aT[pP->GetTeam()]++; } - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { - dbg_msg("game", "Team is NOT balanced (red=%d blue=%d)", t[0], t[1]); - if (game.controller->unbalanced_tick == -1) - game.controller->unbalanced_tick = server_tick(); + dbg_msg("game", "Team is NOT balanced (red=%d blue=%d)", aT[0], aT[1]); + if(GameServer()->m_pController->m_UnbalancedTick == -1) + GameServer()->m_pController->m_UnbalancedTick = Server()->Tick(); return false; } else { - dbg_msg("game", "Team is balanced (red=%d blue=%d)", t[0], t[1]); - game.controller->unbalanced_tick = -1; + dbg_msg("game", "Team is balanced (red=%d blue=%d)", aT[0], aT[1]); + GameServer()->m_pController->m_UnbalancedTick = -1; return true; } } -bool GAMECONTROLLER::can_change_team(PLAYER *pplayer, int jointeam) +bool IGameController::CanChangeTeam(CPlayer *pPlayer, int JoinTeam) { - int t[2] = {0, 0}; + int aT[2] = {0, 0}; - if (!is_teamplay() || jointeam == -1 || !config.sv_teambalance_time) + if (!IsTeamplay() || JoinTeam == -1 || !g_Config.m_SvTeambalanceTime) return true; for(int i = 0; i < MAX_CLIENTS; i++) { - PLAYER *p = game.players[i]; - if(p && p->team != -1) - t[p->team]++; + CPlayer *pP = GameServer()->m_apPlayers[i]; + if(pP && pP->GetTeam() != -1) + aT[pP->GetTeam()]++; } // simulate what would happen if changed team - t[jointeam]++; - if (pplayer->team != -1) - t[jointeam^1]--; + aT[JoinTeam]++; + if (pPlayer->GetTeam() != -1) + aT[JoinTeam^1]--; // there is a player-difference of at least 2 - if(abs(t[0]-t[1]) >= 2) + if(absolute(aT[0]-aT[1]) >= 2) { // player wants to join team with less players - if ((t[0] < t[1] && jointeam == 0) || (t[0] > t[1] && jointeam == 1)) + if ((aT[0] < aT[1] && JoinTeam == 0) || (aT[0] > aT[1] && JoinTeam == 1)) return true; else return false; @@ -610,62 +617,60 @@ bool GAMECONTROLLER::can_change_team(PLAYER *pplayer, int jointeam) return true; } -void GAMECONTROLLER::do_player_score_wincheck() +void IGameController::DoPlayerScoreWincheck() { - if(game_over_tick == -1 && !warmup) + if(m_GameOverTick == -1 && !m_Warmup) { // gather some stats - int topscore = 0; - int topscore_count = 0; + int Topscore = 0; + int TopscoreCount = 0; for(int i = 0; i < MAX_CLIENTS; i++) { - if(game.players[i]) + if(GameServer()->m_apPlayers[i]) { - if(game.players[i]->score > topscore) + if(GameServer()->m_apPlayers[i]->m_Score > Topscore) { - topscore = game.players[i]->score; - topscore_count = 1; + Topscore = GameServer()->m_apPlayers[i]->m_Score; + TopscoreCount = 1; } - else if(game.players[i]->score == topscore) - topscore_count++; + else if(GameServer()->m_apPlayers[i]->m_Score == Topscore) + TopscoreCount++; } } // check score win condition - if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + if((g_Config.m_SvScorelimit > 0 && Topscore >= g_Config.m_SvScorelimit) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(topscore_count == 1) - endround(); + if(TopscoreCount == 1) + EndRound(); else - sudden_death = 1; + m_SuddenDeath = 1; } } } -void GAMECONTROLLER::do_team_score_wincheck() +void IGameController::DoTeamScoreWincheck() { - if(game_over_tick == -1 && !warmup) + if(m_GameOverTick == -1 && !m_Warmup) { // check score win condition - if((config.sv_scorelimit > 0 && (teamscore[0] >= config.sv_scorelimit || teamscore[1] >= config.sv_scorelimit)) || - (config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60)) + if((g_Config.m_SvScorelimit > 0 && (m_aTeamscore[0] >= g_Config.m_SvScorelimit || m_aTeamscore[1] >= g_Config.m_SvScorelimit)) || + (g_Config.m_SvTimelimit > 0 && (Server()->Tick()-m_RoundStartTick) >= g_Config.m_SvTimelimit*Server()->TickSpeed()*60)) { - if(teamscore[0] != teamscore[1]) - endround(); + if(m_aTeamscore[0] != m_aTeamscore[1]) + EndRound(); else - sudden_death = 1; + m_SuddenDeath = 1; } } } -int GAMECONTROLLER::clampteam(int team) +int IGameController::ClampTeam(int Team) { - if(team < 0) // spectator + if(Team < 0) // spectator return -1; - if(is_teamplay()) - return team&1; + if(IsTeamplay()) + return Team&1; return 0; } - -GAMECONTROLLER *gamecontroller = 0; diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h new file mode 100644 index 00000000..0624dcab --- /dev/null +++ b/src/game/server/gamecontroller.h @@ -0,0 +1,145 @@ +#ifndef GAME_SERVER_GAMECONTROLLER_H +#define GAME_SERVER_GAMECONTROLLER_H + +#include <base/vmath.h> + +/* + Class: Game Controller + Controls the main game logic. Keeping track of team and player score, + winning conditions and specific game logic. +*/ +class IGameController +{ + vec2 m_aaSpawnPoints[3][64]; + int m_aNumSpawnPoints[3]; + + class CGameContext *m_pGameServer; + class IServer *m_pServer; + +protected: + CGameContext *GameServer() const { return m_pGameServer; } + IServer *Server() const { return m_pServer; } + + struct CSpawnEval + { + CSpawnEval() + { + m_Got = false; + m_FriendlyTeam = -1; + m_Pos = vec2(100,100); + } + + vec2 m_Pos; + bool m_Got; + int m_FriendlyTeam; + float m_Score; + }; + + float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos); + void EvaluateSpawnType(CSpawnEval *pEval, int Type); + bool EvaluateSpawn(class CPlayer *pP, vec2 *pPos); + + void CycleMap(); + void ResetGame(); + + char m_aMapWish[128]; + + + int m_RoundStartTick; + int m_GameOverTick; + int m_SuddenDeath; + + int m_aTeamscore[2]; + + int m_Warmup; + int m_RoundCount; + + int m_GameFlags; + int m_UnbalancedTick; + bool m_ForceBalanced; + +public: + const char *m_pGameType; + + bool IsTeamplay() const; + + IGameController(class CGameContext *pGameServer); + virtual ~IGameController(); + + void DoTeamScoreWincheck(); + void DoPlayerScoreWincheck(); + + void DoWarmup(int Seconds); + + void StartRound(); + void EndRound(); + void ChangeMap(const char *pToMap); + + bool IsFriendlyFire(int Cid1, int Cid2); + + bool IsForceBalanced(); + + /* + + */ + virtual bool CanBeMovedOnBalance(int Cid); + + virtual void Tick(); + + virtual void Snap(int SnappingClient); + + /* + Function: on_entity + Called when the map is loaded to process an entity + in the map. + + Arguments: + index - Entity index. + pos - Where the entity is located in the world. + + Returns: + bool? + */ + virtual bool OnEntity(int Index, vec2 Pos); + + /* + Function: on_CCharacter_spawn + Called when a CCharacter spawns into the game world. + + Arguments: + chr - The CCharacter that was spawned. + */ + virtual void OnCharacterSpawn(class CCharacter *pChr); + + /* + Function: on_CCharacter_death + Called when a CCharacter in the world dies. + + Arguments: + victim - The CCharacter that died. + killer - The player that killed it. + weapon - What weapon that killed it. Can be -1 for undefined + weapon when switching team or player suicides. + */ + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + + + virtual void OnPlayerInfoChange(class CPlayer *pP); + + // + virtual bool CanSpawn(class CPlayer *pP, vec2 *pPos); + + /* + + */ + virtual const char *GetTeamName(int Team); + virtual int GetAutoTeam(int NotThisId); + virtual bool CanJoinTeam(int Team, int NotThisId); + bool CheckTeamBalance(); + bool CanChangeTeam(CPlayer *pPplayer, int JoinTeam); + int ClampTeam(int Team); + + virtual void PostReset(); +}; + +#endif diff --git a/src/game/server/gamecontroller.hpp b/src/game/server/gamecontroller.hpp deleted file mode 100644 index 26b8a81f..00000000 --- a/src/game/server/gamecontroller.hpp +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef GAME_SERVER_GAMECONTROLLER_H -#define GAME_SERVER_GAMECONTROLLER_H - -#include <base/vmath.hpp> - -/* - Class: Game Controller - Controls the main game logic. Keeping track of team and player score, - winning conditions and specific game logic. -*/ -class GAMECONTROLLER -{ - vec2 spawn_points[3][64]; - int num_spawn_points[3]; -protected: - struct SPAWNEVAL - { - SPAWNEVAL() - { - got = false; - friendly_team = -1; - pos = vec2(100,100); - } - - vec2 pos; - bool got; - int friendly_team; - float score; - }; - - float evaluate_spawn_pos(SPAWNEVAL *eval, vec2 pos); - void evaluate_spawn_type(SPAWNEVAL *eval, int type); - bool evaluate_spawn(class PLAYER *p, vec2 *pos); - - void cyclemap(); - void resetgame(); - - char map_wish[128]; - - - int round_start_tick; - int game_over_tick; - int sudden_death; - - int teamscore[2]; - - int warmup; - int round_count; - - int game_flags; - int unbalanced_tick; - bool force_balanced; - -public: - const char *gametype; - - bool is_teamplay() const; - - GAMECONTROLLER(); - virtual ~GAMECONTROLLER(); - - void do_team_score_wincheck(); - void do_player_score_wincheck(); - - void do_warmup(int seconds); - - void startround(); - void endround(); - void change_map(const char *to_map); - - bool is_friendly_fire(int cid1, int cid2); - - bool is_force_balanced(); - - /* - - */ - virtual void tick(); - - virtual void snap(int snapping_client); - - /* - Function: on_entity - Called when the map is loaded to process an entity - in the map. - - Arguments: - index - Entity index. - pos - Where the entity is located in the world. - - Returns: - bool? - */ - virtual bool on_entity(int index, vec2 pos); - - /* - Function: on_character_spawn - Called when a character spawns into the game world. - - Arguments: - chr - The character that was spawned. - */ - virtual void on_character_spawn(class CHARACTER *chr); - - /* - Function: on_character_death - Called when a character in the world dies. - - Arguments: - victim - The character that died. - killer - The player that killed it. - weapon - What weapon that killed it. Can be -1 for undefined - weapon when switching team or player suicides. - */ - virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); - - - virtual void on_player_info_change(class PLAYER *p); - - // - virtual bool can_spawn(class PLAYER *p, vec2 *pos); - - /* - - */ - virtual const char *get_team_name(int team); - virtual int get_auto_team(int notthisid); - virtual bool can_join_team(int team, int notthisid); - bool check_team_balance(); - bool can_change_team(PLAYER *pplayer, int jointeam); - int clampteam(int team); - - virtual void post_reset(); -}; - -#endif diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index b2146b51..3da88342 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,188 +1,205 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/mapitems.hpp> -#include <game/server/entities/character.hpp> -#include <game/server/player.hpp> -#include <game/server/gamecontext.hpp> -#include "ctf.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/mapitems.h> +#include <game/server/entities/character.h> +#include <game/server/player.h> +#include <game/server/gamecontext.h> +#include "ctf.h" -GAMECONTROLLER_CTF::GAMECONTROLLER_CTF() +CGameControllerCTF::CGameControllerCTF(class CGameContext *pGameServer) +: IGameController(pGameServer) { - flags[0] = 0; - flags[1] = 0; - gametype = "CTF"; - game_flags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS; + m_apFlags[0] = 0; + m_apFlags[1] = 0; + m_pGameType = "CTF"; + m_GameFlags = GAMEFLAG_TEAMS|GAMEFLAG_FLAGS; } -bool GAMECONTROLLER_CTF::on_entity(int index, vec2 pos) +bool CGameControllerCTF::OnEntity(int Index, vec2 Pos) { - if(GAMECONTROLLER::on_entity(index, pos)) + if(IGameController::OnEntity(Index, Pos)) return true; - int team = -1; - if(index == ENTITY_FLAGSTAND_RED) team = 0; - if(index == ENTITY_FLAGSTAND_BLUE) team = 1; - if(team == -1) + int Team = -1; + if(Index == ENTITY_FLAGSTAND_RED) Team = 0; + if(Index == ENTITY_FLAGSTAND_BLUE) Team = 1; + if(Team == -1) return false; - FLAG *f = new FLAG(team); - f->stand_pos = pos; - f->pos = pos; - flags[team] = f; + CFlag *F = new CFlag(&GameServer()->m_World, Team); + F->m_StandPos = Pos; + F->m_Pos = Pos; + m_apFlags[Team] = F; + GameServer()->m_World.InsertEntity(F); return true; } -int GAMECONTROLLER_CTF::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weaponid) +int CGameControllerCTF::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int WeaponID) { - GAMECONTROLLER::on_character_death(victim, killer, weaponid); - int had_flag = 0; + IGameController::OnCharacterDeath(pVictim, pKiller, WeaponID); + int HadFlag = 0; // drop flags - for(int fi = 0; fi < 2; fi++) + for(int i = 0; i < 2; i++) { - FLAG *f = flags[fi]; - if(f && killer && f->carrying_character == killer->get_character()) - had_flag |= 2; - if(f && f->carrying_character == victim) + CFlag *F = m_apFlags[i]; + if(F && pKiller && pKiller->GetCharacter() && F->m_pCarryingCCharacter == pKiller->GetCharacter()) + HadFlag |= 2; + if(F && F->m_pCarryingCCharacter == pVictim) { - game.create_sound_global(SOUND_CTF_DROP); - f->drop_tick = server_tick(); - f->carrying_character = 0; - f->vel = vec2(0,0); + GameServer()->CreateSoundGlobal(SOUND_CTF_DROP); + F->m_DropTick = Server()->Tick(); + F->m_pCarryingCCharacter = 0; + F->m_Vel = vec2(0,0); - if(killer && killer->team != victim->team) - killer->score++; + if(pKiller && pKiller->GetTeam() != pVictim->GetPlayer()->GetTeam()) + pKiller->m_Score++; - had_flag |= 1; + HadFlag |= 1; } } - return had_flag; + return HadFlag; +} + +bool CGameControllerCTF::CanBeMovedOnBalance(int Cid) +{ + CCharacter* Character = GameServer()->m_apPlayers[Cid]->GetCharacter(); + if(Character) + { + for(int fi = 0; fi < 2; fi++) + { + CFlag *F = m_apFlags[fi]; + if(F->m_pCarryingCCharacter == Character) + return false; + } + } + return true; } -void GAMECONTROLLER_CTF::tick() +void CGameControllerCTF::Tick() { - GAMECONTROLLER::tick(); + IGameController::Tick(); - do_team_score_wincheck(); + DoTeamScoreWincheck(); for(int fi = 0; fi < 2; fi++) { - FLAG *f = flags[fi]; + CFlag *F = m_apFlags[fi]; - if(!f) + if(!F) continue; // flag hits death-tile, reset it - if(col_get((int)f->pos.x, (int)f->pos.y)&COLFLAG_DEATH) + if(GameServer()->Collision()->GetCollisionAt(F->m_Pos.x, F->m_Pos.y)&CCollision::COLFLAG_DEATH) { - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); continue; } // - if(f->carrying_character) + if(F->m_pCarryingCCharacter) { // update flag position - f->pos = f->carrying_character->pos; + F->m_Pos = F->m_pCarryingCCharacter->m_Pos; - if(flags[fi^1] && flags[fi^1]->at_stand) + if(m_apFlags[fi^1] && m_apFlags[fi^1]->m_AtStand) { - if(distance(f->pos, flags[fi^1]->pos) < 32) + if(distance(F->m_Pos, m_apFlags[fi^1]->m_Pos) < 32) { // CAPTURE! \o/ - teamscore[fi^1] += 100; - f->carrying_character->player->score += 5; + m_aTeamscore[fi^1] += 100; + F->m_pCarryingCCharacter->GetPlayer()->m_Score += 5; dbg_msg("game", "flag_capture player='%d:%s'", - f->carrying_character->player->client_id, - server_clientname(f->carrying_character->player->client_id)); + F->m_pCarryingCCharacter->GetPlayer()->GetCID(), + Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); - char buf[512]; - float capture_time = (server_tick() - f->grab_tick)/(float)server_tickspeed(); - if(capture_time <= 60) + char Buf[512]; + float CaptureTime = (Server()->Tick() - F->m_GrabTick)/(float)Server()->TickSpeed(); + if(CaptureTime <= 60) { - str_format(buf, sizeof(buf), "the %s flag was captured by %s (%d.%s%d seconds)", fi ? "blue" : "red", server_clientname(f->carrying_character->player->client_id), (int)capture_time%60, ((int)(capture_time*100)%100)<10?"0":"", (int)(capture_time*100)%100); + str_format(Buf, sizeof(Buf), "The %s flag was captured by %s (%d.%s%d seconds)", fi ? "blue" : "red", Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID()), (int)CaptureTime%60, ((int)(CaptureTime*100)%100)<10?"0":"", (int)(CaptureTime*100)%100); } else { - str_format(buf, sizeof(buf), "the %s flag was captured by %s", fi ? "blue" : "red", server_clientname(f->carrying_character->player->client_id)); + str_format(Buf, sizeof(Buf), "The %s flag was captured by %s", fi ? "blue" : "red", Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); } - game.send_chat(-1, -2, buf); + GameServer()->SendChat(-1, -2, Buf); for(int i = 0; i < 2; i++) - flags[i]->reset(); + m_apFlags[i]->Reset(); - game.create_sound_global(SOUND_CTF_CAPTURE); + GameServer()->CreateSoundGlobal(SOUND_CTF_CAPTURE); } - } + } } else { - CHARACTER *close_characters[MAX_CLIENTS]; - int num = game.world.find_entities(f->pos, 32.0f, (ENTITY**)close_characters, MAX_CLIENTS, NETOBJTYPE_CHARACTER); - for(int i = 0; i < num; i++) + CCharacter *apCloseCCharacters[MAX_CLIENTS]; + int Num = GameServer()->m_World.FindEntities(F->m_Pos, 32.0f, (CEntity**)apCloseCCharacters, MAX_CLIENTS, NETOBJTYPE_CHARACTER); + for(int i = 0; i < Num; i++) { - if(!close_characters[i]->alive || close_characters[i]->player->team == -1 || col_intersect_line(f->pos, close_characters[i]->pos, NULL, NULL)) + if(!apCloseCCharacters[i]->IsAlive() || apCloseCCharacters[i]->GetPlayer()->GetTeam() == -1 || GameServer()->Collision()->IntersectLine(F->m_Pos, apCloseCCharacters[i]->m_Pos, NULL, NULL)) continue; - if(close_characters[i]->team == f->team) + if(apCloseCCharacters[i]->GetPlayer()->GetTeam() == F->m_Team) { // return the flag - if(!f->at_stand) + if(!F->m_AtStand) { - CHARACTER *chr = close_characters[i]; - chr->player->score += 1; + CCharacter *pChr = apCloseCCharacters[i]; + pChr->GetPlayer()->m_Score += 1; dbg_msg("game", "flag_return player='%d:%s'", - chr->player->client_id, - server_clientname(chr->player->client_id)); + pChr->GetPlayer()->GetCID(), + Server()->ClientName(pChr->GetPlayer()->GetCID())); - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); } } else { // take the flag - if(f->at_stand) + if(F->m_AtStand) { - teamscore[fi^1]++; - f->grab_tick = server_tick(); + m_aTeamscore[fi^1]++; + F->m_GrabTick = Server()->Tick(); } - f->at_stand = 0; - f->carrying_character = close_characters[i]; - f->carrying_character->player->score += 1; + + F->m_AtStand = 0; + F->m_pCarryingCCharacter = apCloseCCharacters[i]; + F->m_pCarryingCCharacter->GetPlayer()->m_Score += 1; dbg_msg("game", "flag_grab player='%d:%s'", - f->carrying_character->player->client_id, - server_clientname(f->carrying_character->player->client_id)); + F->m_pCarryingCCharacter->GetPlayer()->GetCID(), + Server()->ClientName(F->m_pCarryingCCharacter->GetPlayer()->GetCID())); for(int c = 0; c < MAX_CLIENTS; c++) { - if(!game.players[c]) + if(!GameServer()->m_apPlayers[c]) continue; - if(game.players[c]->team == fi) - game.create_sound_global(SOUND_CTF_GRAB_EN, game.players[c]->client_id); + if(GameServer()->m_apPlayers[c]->GetTeam() == fi) + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_EN, GameServer()->m_apPlayers[c]->GetCID()); else - game.create_sound_global(SOUND_CTF_GRAB_PL, game.players[c]->client_id); + GameServer()->CreateSoundGlobal(SOUND_CTF_GRAB_PL, GameServer()->m_apPlayers[c]->GetCID()); } break; } } - if(!f->carrying_character && !f->at_stand) + if(!F->m_pCarryingCCharacter && !F->m_AtStand) { - if(server_tick() > f->drop_tick + server_tickspeed()*30) + if(Server()->Tick() > F->m_DropTick + Server()->TickSpeed()*30) { - game.create_sound_global(SOUND_CTF_RETURN); - f->reset(); + GameServer()->CreateSoundGlobal(SOUND_CTF_RETURN); + F->Reset(); } else { - f->vel.y += game.world.core.tuning.gravity; - move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f); + F->m_Vel.y += GameServer()->m_World.m_Core.m_Tuning.m_Gravity; + GameServer()->Collision()->MoveBox(&F->m_Pos, &F->m_Vel, vec2(F->m_PhysSize, F->m_PhysSize), 0.5f); } } } @@ -190,39 +207,36 @@ void GAMECONTROLLER_CTF::tick() } // Flag -FLAG::FLAG(int _team) -: ENTITY(NETOBJTYPE_FLAG) +CFlag::CFlag(CGameWorld *pGameWorld, int Team) +: CEntity(pGameWorld, NETOBJTYPE_FLAG) { - team = _team; - proximity_radius = phys_size; - carrying_character = 0x0; - grab_tick = 0; - - reset(); + m_Team = Team; + m_ProximityRadius = m_PhysSize; + m_pCarryingCCharacter = 0x0; + m_GrabTick = 0; - // TODO: should this be done here? - game.world.insert_entity(this); + Reset(); } -void FLAG::reset() +void CFlag::Reset() { - carrying_character = 0x0; - at_stand = 1; - pos = stand_pos; - vel = vec2(0,0); - grab_tick = 0; + m_pCarryingCCharacter = 0x0; + m_AtStand = 1; + m_Pos = m_StandPos; + m_Vel = vec2(0,0); + m_GrabTick = 0; } -void FLAG::snap(int snapping_client) +void CFlag::Snap(int SnappingClient) { - NETOBJ_FLAG *flag = (NETOBJ_FLAG *)snap_new_item(NETOBJTYPE_FLAG, team, sizeof(NETOBJ_FLAG)); - flag->x = (int)pos.x; - flag->y = (int)pos.y; - flag->team = team; - flag->carried_by = -1; + CNetObj_Flag *pFlag = (CNetObj_Flag *)Server()->SnapNewItem(NETOBJTYPE_FLAG, m_Team, sizeof(CNetObj_Flag)); + pFlag->m_X = (int)m_Pos.x; + pFlag->m_Y = (int)m_Pos.y; + pFlag->m_Team = m_Team; + pFlag->m_CarriedBy = -1; - if(at_stand) - flag->carried_by = -2; - else if(carrying_character && carrying_character->player) - flag->carried_by = carrying_character->player->client_id; + if(m_AtStand) + pFlag->m_CarriedBy = -2; + else if(m_pCarryingCCharacter && m_pCarryingCCharacter->GetPlayer()) + pFlag->m_CarriedBy = m_pCarryingCCharacter->GetPlayer()->GetCID(); } diff --git a/src/game/server/gamemodes/ctf.h b/src/game/server/gamemodes/ctf.h new file mode 100644 index 00000000..b86ad688 --- /dev/null +++ b/src/game/server/gamemodes/ctf.h @@ -0,0 +1,38 @@ +#ifndef GAME_SERVER_GAMEMODES_CTF_H +#define GAME_SERVER_GAMEMODES_CTF_H +#include <game/server/gamecontroller.h> +#include <game/server/entity.h> + +class CGameControllerCTF : public IGameController +{ +public: + class CFlag *m_apFlags[2]; + + CGameControllerCTF(class CGameContext *pGameServer); + virtual bool CanBeMovedOnBalance(int Cid); + virtual void Tick(); + + virtual bool OnEntity(int Index, vec2 Pos); + virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); +}; + +// TODO: move to seperate file +class CFlag : public CEntity +{ +public: + static const int m_PhysSize = 14; + CCharacter *m_pCarryingCCharacter; + vec2 m_Vel; + vec2 m_StandPos; + + int m_Team; + int m_AtStand; + int m_DropTick; + int m_GrabTick; + + CFlag(CGameWorld *pGameWorld, int Team); + + virtual void Reset(); + virtual void Snap(int SnappingClient); +}; +#endif diff --git a/src/game/server/gamemodes/ctf.hpp b/src/game/server/gamemodes/ctf.hpp deleted file mode 100644 index 67a098a4..00000000 --- a/src/game/server/gamemodes/ctf.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> -#include <game/server/entity.hpp> - -class GAMECONTROLLER_CTF : public GAMECONTROLLER -{ -public: - class FLAG *flags[2]; - - GAMECONTROLLER_CTF(); - virtual void tick(); - - virtual bool on_entity(int index, vec2 pos); - virtual int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); -}; - -// TODO: move to seperate file -class FLAG : public ENTITY -{ -public: - static const int phys_size = 14; - CHARACTER *carrying_character; - vec2 vel; - vec2 stand_pos; - - int team; - int at_stand; - int drop_tick; - int grab_tick; - - FLAG(int _team); - - virtual void reset(); - virtual void snap(int snapping_client); -}; diff --git a/src/game/server/gamemodes/dm.cpp b/src/game/server/gamemodes/dm.cpp index 15c0b987..173ef5fd 100644 --- a/src/game/server/gamemodes/dm.cpp +++ b/src/game/server/gamemodes/dm.cpp @@ -1,14 +1,15 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "dm.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "dm.h" -GAMECONTROLLER_DM::GAMECONTROLLER_DM() +CGameControllerDM::CGameControllerDM(class CGameContext *pGameServer) +: IGameController(pGameServer) { - gametype = "DM"; + m_pGameType = "DM"; } -void GAMECONTROLLER_DM::tick() +void CGameControllerDM::Tick() { - do_player_score_wincheck(); - GAMECONTROLLER::tick(); + DoPlayerScoreWincheck(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/dm.h b/src/game/server/gamemodes/dm.h new file mode 100644 index 00000000..f2854680 --- /dev/null +++ b/src/game/server/gamemodes/dm.h @@ -0,0 +1,11 @@ +#ifndef GAME_SERVER_GAMEMODES_DM_H +#define GAME_SERVER_GAMEMODES_DM_H +#include <game/server/gamecontroller.h> + +class CGameControllerDM : public IGameController +{ +public: + CGameControllerDM(class CGameContext *pGameServer); + virtual void Tick(); +}; +#endif diff --git a/src/game/server/gamemodes/dm.hpp b/src/game/server/gamemodes/dm.hpp deleted file mode 100644 index 6fb25f61..00000000 --- a/src/game/server/gamemodes/dm.hpp +++ /dev/null @@ -1,10 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -class GAMECONTROLLER_DM : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_DM(); - virtual void tick(); -}; diff --git a/src/game/server/gamemodes/mod.cpp b/src/game/server/gamemodes/mod.cpp index 87b37411..44d0effc 100644 --- a/src/game/server/gamemodes/mod.cpp +++ b/src/game/server/gamemodes/mod.cpp @@ -1,20 +1,21 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "mod.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include "mod.h" -GAMECONTROLLER_MOD::GAMECONTROLLER_MOD() +CGameControllerMOD::CGameControllerMOD(class CGameContext *pGameServer) +: IGameController(pGameServer) { // Exchange this to a string that identifies your game mode. // DM, TDM and CTF are reserved for teeworlds original modes. - gametype = "MOD"; + m_pGameType = "MOD"; - //game_flags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode + //m_GameFlags = GAMEFLAG_TEAMS; // GAMEFLAG_TEAMS makes it a two-team gamemode } -void GAMECONTROLLER_MOD::tick() +void CGameControllerMOD::Tick() { // this is the main part of the gamemode, this function is run every tick - do_player_score_wincheck(); // checks for winners, no teams version - //do_team_score_wincheck(); // checks for winners, two teams version + DoPlayerScoreWincheck(); // checks for winners, no teams version + //DoTeamScoreWincheck(); // checks for winners, two teams version - GAMECONTROLLER::tick(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/mod.h b/src/game/server/gamemodes/mod.h new file mode 100644 index 00000000..86dff78b --- /dev/null +++ b/src/game/server/gamemodes/mod.h @@ -0,0 +1,14 @@ +#ifndef GAME_SERVER_GAMEMODES_MOD_H +#define GAME_SERVER_GAMEMODES_MOD_H +#include <game/server/gamecontroller.h> + +// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want +// todo a modification with their base as well. +class CGameControllerMOD : public IGameController +{ +public: + CGameControllerMOD(class CGameContext *pGameServer); + virtual void Tick(); + // add more virtual functions here if you wish +}; +#endif diff --git a/src/game/server/gamemodes/mod.hpp b/src/game/server/gamemodes/mod.hpp deleted file mode 100644 index 9915a615..00000000 --- a/src/game/server/gamemodes/mod.hpp +++ /dev/null @@ -1,13 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -// you can subclass GAMECONTROLLER_CTF, GAMECONTROLLER_TDM etc if you want -// todo a modification with their base as well. -class GAMECONTROLLER_MOD : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_MOD(); - virtual void tick(); - // add more virtual functions here if you wish -}; diff --git a/src/game/server/gamemodes/tdm.cpp b/src/game/server/gamemodes/tdm.cpp index 72605000..b54e3ac0 100644 --- a/src/game/server/gamemodes/tdm.cpp +++ b/src/game/server/gamemodes/tdm.cpp @@ -1,34 +1,33 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <engine/e_server_interface.h> -#include <game/server/entities/character.hpp> -#include <game/server/player.hpp> -#include "tdm.hpp" +// copyright (c) 2007 magnus auvinen, see licence.txt for more info +#include <game/server/entities/character.h> +#include <game/server/player.h> +#include "tdm.h" -GAMECONTROLLER_TDM::GAMECONTROLLER_TDM() +CGameControllerTDM::CGameControllerTDM(class CGameContext *pGameServer) : IGameController(pGameServer) { - gametype = "TDM"; - game_flags = GAMEFLAG_TEAMS; + m_pGameType = "TDM"; + m_GameFlags = GAMEFLAG_TEAMS; } -int GAMECONTROLLER_TDM::on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon) +int CGameControllerTDM::OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon) { - GAMECONTROLLER::on_character_death(victim, killer, weapon); + IGameController::OnCharacterDeath(pVictim, pKiller, Weapon); - if(weapon != WEAPON_GAME) + if(Weapon != WEAPON_GAME) { // do team scoring - if(killer == victim->player || killer->team == victim->player->team) - teamscore[killer->team&1]--; // klant arschel + if(pKiller == pVictim->GetPlayer() || pKiller->GetTeam() == pVictim->GetPlayer()->GetTeam()) + m_aTeamscore[pKiller->GetTeam()&1]--; // klant arschel else - teamscore[killer->team&1]++; // good shit + m_aTeamscore[pKiller->GetTeam()&1]++; // good shit } return 0; } -void GAMECONTROLLER_TDM::tick() +void CGameControllerTDM::Tick() { - do_team_score_wincheck(); - GAMECONTROLLER::tick(); + DoTeamScoreWincheck(); + IGameController::Tick(); } diff --git a/src/game/server/gamemodes/tdm.h b/src/game/server/gamemodes/tdm.h new file mode 100644 index 00000000..2d149456 --- /dev/null +++ b/src/game/server/gamemodes/tdm.h @@ -0,0 +1,13 @@ +#ifndef GAME_SERVER_GAMEMODES_TDM_H +#define GAME_SERVER_GAMEMODES_TDM_H +#include <game/server/gamecontroller.h> + +class CGameControllerTDM : public IGameController +{ +public: + CGameControllerTDM(class CGameContext *pGameServer); + + int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon); + virtual void Tick(); +}; +#endif diff --git a/src/game/server/gamemodes/tdm.hpp b/src/game/server/gamemodes/tdm.hpp deleted file mode 100644 index 51c47ca5..00000000 --- a/src/game/server/gamemodes/tdm.hpp +++ /dev/null @@ -1,12 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <game/server/gamecontroller.hpp> - -class GAMECONTROLLER_TDM : public GAMECONTROLLER -{ -public: - GAMECONTROLLER_TDM(); - - int on_character_death(class CHARACTER *victim, class PLAYER *killer, int weapon); - virtual void tick(); -}; diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp index 9e76f14b..42c19487 100644 --- a/src/game/server/gameworld.cpp +++ b/src/game/server/gameworld.cpp @@ -1,217 +1,226 @@ -#include "gameworld.hpp" -#include "entity.hpp" -#include "gamecontext.hpp" +#include "gameworld.h" +#include "entity.h" +#include "gamecontext.h" ////////////////////////////////////////////////// // game world ////////////////////////////////////////////////// -GAMEWORLD::GAMEWORLD() +CGameWorld::CGameWorld() { - paused = false; - reset_requested = false; - first_entity = 0x0; + m_pGameServer = 0x0; + m_pServer = 0x0; + + m_Paused = false; + m_ResetRequested = false; + m_pFirstEntity = 0x0; for(int i = 0; i < NUM_ENT_TYPES; i++) - first_entity_types[i] = 0; + m_apFirstEntityTypes[i] = 0; } -GAMEWORLD::~GAMEWORLD() +CGameWorld::~CGameWorld() { // delete all entities - while(first_entity) - delete first_entity; + while(m_pFirstEntity) + delete m_pFirstEntity; +} + +void CGameWorld::SetGameServer(CGameContext *pGameServer) +{ + m_pGameServer = pGameServer; + m_pServer = m_pGameServer->Server(); } -ENTITY *GAMEWORLD::find_first(int type) +CEntity *CGameWorld::FindFirst(int Type) { - return first_entity_types[type]; + return m_apFirstEntityTypes[Type]; } -int GAMEWORLD::find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type) +int CGameWorld::FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type) { - int num = 0; - for(ENTITY *ent = (type<0) ? first_entity : first_entity_types[type]; - ent; ent = (type<0) ? ent->next_entity : ent->next_type_entity) + int Num = 0; + for(CEntity *pEnt = (Type<0) ? m_pFirstEntity : m_apFirstEntityTypes[Type]; + pEnt; pEnt = (Type<0) ? pEnt->m_pNextEntity : pEnt->m_pNextTypeEntity) { - if(distance(ent->pos, pos) < radius+ent->proximity_radius) + if(distance(pEnt->m_Pos, Pos) < Radius+pEnt->m_ProximityRadius) { - ents[num] = ent; - num++; - if(num == max) + ppEnts[Num] = pEnt; + Num++; + if(Num == Max) break; } } - return num; + return Num; } -void GAMEWORLD::insert_entity(ENTITY *ent) +void CGameWorld::InsertEntity(CEntity *pEnt) { - ENTITY *cur = first_entity; - while(cur) + CEntity *pCur = m_pFirstEntity; + while(pCur) { - dbg_assert(cur != ent, "err"); - cur = cur->next_entity; + dbg_assert(pCur != pEnt, "err"); + pCur = pCur->m_pNextEntity; } // insert it - if(first_entity) - first_entity->prev_entity = ent; - ent->next_entity = first_entity; - ent->prev_entity = 0x0; - first_entity = ent; + if(m_pFirstEntity) + m_pFirstEntity->m_pPrevEntity = pEnt; + pEnt->m_pNextEntity = m_pFirstEntity; + pEnt->m_pPrevEntity = 0x0; + m_pFirstEntity = pEnt; // into typelist aswell - if(first_entity_types[ent->objtype]) - first_entity_types[ent->objtype]->prev_type_entity = ent; - ent->next_type_entity = first_entity_types[ent->objtype]; - ent->prev_type_entity = 0x0; - first_entity_types[ent->objtype] = ent; + if(m_apFirstEntityTypes[pEnt->m_Objtype]) + m_apFirstEntityTypes[pEnt->m_Objtype]->m_pPrevTypeEntity = pEnt; + pEnt->m_pNextTypeEntity = m_apFirstEntityTypes[pEnt->m_Objtype]; + pEnt->m_pPrevTypeEntity = 0x0; + m_apFirstEntityTypes[pEnt->m_Objtype] = pEnt; } -void GAMEWORLD::destroy_entity(ENTITY *ent) +void CGameWorld::DestroyEntity(CEntity *pEnt) { - ent->marked_for_destroy = true; + pEnt->m_MarkedForDestroy = true; } -void GAMEWORLD::remove_entity(ENTITY *ent) +void CGameWorld::RemoveEntity(CEntity *pEnt) { // not in the list - if(!ent->next_entity && !ent->prev_entity && first_entity != ent) + if(!pEnt->m_pNextEntity && !pEnt->m_pPrevEntity && m_pFirstEntity != pEnt) return; // remove - if(ent->prev_entity) - ent->prev_entity->next_entity = ent->next_entity; + if(pEnt->m_pPrevEntity) + pEnt->m_pPrevEntity->m_pNextEntity = pEnt->m_pNextEntity; else - first_entity = ent->next_entity; - if(ent->next_entity) - ent->next_entity->prev_entity = ent->prev_entity; + m_pFirstEntity = pEnt->m_pNextEntity; + if(pEnt->m_pNextEntity) + pEnt->m_pNextEntity->m_pPrevEntity = pEnt->m_pPrevEntity; - if(ent->prev_type_entity) - ent->prev_type_entity->next_type_entity = ent->next_type_entity; + if(pEnt->m_pPrevTypeEntity) + pEnt->m_pPrevTypeEntity->m_pNextTypeEntity = pEnt->m_pNextTypeEntity; else - first_entity_types[ent->objtype] = ent->next_type_entity; - if(ent->next_type_entity) - ent->next_type_entity->prev_type_entity = ent->prev_type_entity; - - ent->next_entity = 0; - ent->prev_entity = 0; - ent->next_type_entity = 0; - ent->prev_type_entity = 0; + m_apFirstEntityTypes[pEnt->m_Objtype] = pEnt->m_pNextTypeEntity; + if(pEnt->m_pNextTypeEntity) + pEnt->m_pNextTypeEntity->m_pPrevTypeEntity = pEnt->m_pPrevTypeEntity; + + pEnt->m_pNextEntity = 0; + pEnt->m_pPrevEntity = 0; + pEnt->m_pNextTypeEntity = 0; + pEnt->m_pPrevTypeEntity = 0; } // -void GAMEWORLD::snap(int snapping_client) +void CGameWorld::Snap(int SnappingClient) { - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->snap(snapping_client); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Snap(SnappingClient); } -void GAMEWORLD::reset() +void CGameWorld::Reset() { // reset all entities - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->reset(); - remove_entities(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Reset(); + RemoveEntities(); - game.controller->post_reset(); - remove_entities(); + GameServer()->m_pController->PostReset(); + RemoveEntities(); - reset_requested = false; + m_ResetRequested = false; } -void GAMEWORLD::remove_entities() +void CGameWorld::RemoveEntities() { // destroy objects marked for destruction - ENTITY *ent = first_entity; - while(ent) + CEntity *pEnt = m_pFirstEntity; + while(pEnt) { - ENTITY *next = ent->next_entity; - if(ent->marked_for_destroy) + CEntity *pNext = pEnt->m_pNextEntity; + if(pEnt->m_MarkedForDestroy) { - remove_entity(ent); - ent->destroy(); + RemoveEntity(pEnt); + pEnt->Destroy(); } - ent = next; + pEnt = pNext; } } -void GAMEWORLD::tick() +void CGameWorld::Tick() { - if(reset_requested) - reset(); + if(m_ResetRequested) + Reset(); - if(!paused) + if(!m_Paused) { - if(game.controller->is_force_balanced()) - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, "Teams have been balanced"); + if(GameServer()->m_pController->IsForceBalanced()) + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced"); // update all objects - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->tick(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->Tick(); - for(ENTITY *ent = first_entity; ent; ent = ent->next_entity) - ent->tick_defered(); + for(CEntity *pEnt = m_pFirstEntity; pEnt; pEnt = pEnt->m_pNextEntity) + pEnt->TickDefered(); } - remove_entities(); + RemoveEntities(); } // TODO: should be more general -CHARACTER *GAMEWORLD::intersect_character(vec2 pos0, vec2 pos1, float radius, vec2& new_pos, ENTITY *notthis) +CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2& NewPos, CEntity *pNotThis) { // Find other players - float closest_len = distance(pos0, pos1) * 100.0f; - vec2 line_dir = normalize(pos1-pos0); - CHARACTER *closest = 0; + float ClosestLen = distance(Pos0, Pos1) * 100.0f; + vec2 LineDir = normalize(Pos1-Pos0); + CCharacter *pClosest = 0; - CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; p; p = (CHARACTER *)p->typenext()) + CCharacter *p = (CCharacter *)FindFirst(NETOBJTYPE_CHARACTER); + for(; p; p = (CCharacter *)p->TypeNext()) { - if(p == notthis) + if(p == pNotThis) continue; - vec2 intersect_pos = closest_point_on_line(pos0, pos1, p->pos); - float len = distance(p->pos, intersect_pos); - if(len < CHARACTER::phys_size+radius) + vec2 IntersectPos = closest_point_on_line(Pos0, Pos1, p->m_Pos); + float Len = distance(p->m_Pos, IntersectPos); + if(Len < p->m_ProximityRadius+Radius) { - if(len < closest_len) + if(Len < ClosestLen) { - new_pos = intersect_pos; - closest_len = len; - closest = p; + NewPos = IntersectPos; + ClosestLen = Len; + pClosest = p; } } } - return closest; + return pClosest; } -CHARACTER *GAMEWORLD::closest_character(vec2 pos, float radius, ENTITY *notthis) +CCharacter *CGameWorld::ClosestCharacter(vec2 Pos, float Radius, CEntity *pNotThis) { // Find other players - float closest_range = radius*2; - CHARACTER *closest = 0; + float ClosestRange = Radius*2; + CCharacter *pClosest = 0; - CHARACTER *p = (CHARACTER *)game.world.find_first(NETOBJTYPE_CHARACTER); - for(; p; p = (CHARACTER *)p->typenext()) + CCharacter *p = (CCharacter *)GameServer()->m_World.FindFirst(NETOBJTYPE_CHARACTER); + for(; p; p = (CCharacter *)p->TypeNext()) { - if(p == notthis) + if(p == pNotThis) continue; - float len = distance(pos, p->pos); - if(len < CHARACTER::phys_size+radius) + float Len = distance(Pos, p->m_Pos); + if(Len < p->m_ProximityRadius+Radius) { - if(len < closest_range) + if(Len < ClosestRange) { - closest_range = len; - closest = p; + ClosestRange = Len; + pClosest = p; } } } - return closest; + return pClosest; } diff --git a/src/game/server/gameworld.hpp b/src/game/server/gameworld.h index 4757ad67..2d1cc4be 100644 --- a/src/game/server/gameworld.hpp +++ b/src/game/server/gameworld.h @@ -1,20 +1,20 @@ #ifndef GAME_SERVER_GAMEWORLD_H #define GAME_SERVER_GAMEWORLD_H -#include <game/gamecore.hpp> +#include <game/gamecore.h> -class ENTITY; -class CHARACTER; +class CEntity; +class CCharacter; /* Class: Game World Tracks all entities in the game. Propagates tick and snap calls to all entities. */ -class GAMEWORLD +class CGameWorld { - void reset(); - void remove_entities(); + void Reset(); + void RemoveEntities(); enum { @@ -22,19 +22,27 @@ class GAMEWORLD }; // TODO: two lists seams kinda not good, shouldn't be needed - ENTITY *first_entity; - ENTITY *first_entity_types[NUM_ENT_TYPES]; + CEntity *m_pFirstEntity; + CEntity *m_apFirstEntityTypes[NUM_ENT_TYPES]; + + class CGameContext *m_pGameServer; + class IServer *m_pServer; public: - bool reset_requested; - bool paused; - WORLD_CORE core; + class CGameContext *GameServer() { return m_pGameServer; } + class IServer *Server() { return m_pServer; } + + bool m_ResetRequested; + bool m_Paused; + CWorldCore m_Core; + + CGameWorld(); + ~CGameWorld(); - GAMEWORLD(); - ~GAMEWORLD(); + void SetGameServer(CGameContext *pGameServer); - ENTITY *find_first() { return first_entity; } - ENTITY *find_first(int type); + CEntity *FindFirst() { return m_pFirstEntity; } + CEntity *FindFirst(int Type); /* Function: find_entities @@ -51,37 +59,37 @@ public: Returns: Number of entities found and added to the ents array. */ - int find_entities(vec2 pos, float radius, ENTITY **ents, int max, int type = -1); + int FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type = -1); /* - Function: interserct_character - Finds the closest character that intersects the line. + Function: interserct_CCharacter + Finds the closest CCharacter that intersects the line. Arguments: pos0 - Start position pos2 - End position - radius - How for from the line the character is allowed to be. + radius - How for from the line the CCharacter is allowed to be. new_pos - Intersection position notthis - Entity to ignore intersecting with Returns: Returns a pointer to the closest hit or NULL of there is no intersection. */ - class CHARACTER *intersect_character(vec2 pos0, vec2 pos1, float radius, vec2 &new_pos, class ENTITY *notthis = 0); + class CCharacter *IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2 &NewPos, class CEntity *pNotThis = 0); /* - Function: closest_character - Finds the closest character to a specific point. + Function: closest_CCharacter + Finds the closest CCharacter to a specific point. Arguments: pos - The center position. - radius - How far off the character is allowed to be + radius - How far off the CCharacter is allowed to be notthis - Entity to ignore Returns: - Returns a pointer to the closest character or NULL if no character is close enough. + Returns a pointer to the closest CCharacter or NULL if no CCharacter is close enough. */ - class CHARACTER *closest_character(vec2 pos, float radius, ENTITY *notthis); + class CCharacter *ClosestCharacter(vec2 Pos, float Radius, CEntity *ppNotThis); /* Function: insert_entity @@ -90,7 +98,7 @@ public: Arguments: entity - Entity to add */ - void insert_entity(ENTITY *entity); + void InsertEntity(CEntity *pEntity); /* Function: remove_entity @@ -99,7 +107,7 @@ public: Arguments: entity - Entity to remove */ - void remove_entity(ENTITY *entity); + void RemoveEntity(CEntity *pEntity); /* Function: destroy_entity @@ -108,7 +116,7 @@ public: Arguments: entity - Entity to destroy */ - void destroy_entity(ENTITY *entity); + void DestroyEntity(CEntity *pEntity); /* Function: snap @@ -119,7 +127,7 @@ public: snapping_client - ID of the client which snapshot is being created. */ - void snap(int snapping_client); + void Snap(int SnappingClient); /* Function: tick @@ -127,7 +135,7 @@ public: the world to the next tick. */ - void tick(); + void Tick(); }; #endif diff --git a/src/game/server/hooks.cpp b/src/game/server/hooks.cpp deleted file mode 100644 index 89c95f79..00000000 --- a/src/game/server/hooks.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <base/math.hpp> - -#include <engine/e_config.h> -#include <engine/e_server_interface.h> -#include <engine/e_memheap.h> - -#include <game/version.hpp> -#include <game/collision.hpp> -#include <game/layers.hpp> - -#include <game/gamecore.hpp> - -#include "gamecontext.hpp" -#include "gamemodes/dm.hpp" -#include "gamemodes/tdm.hpp" -#include "gamemodes/ctf.hpp" -#include "gamemodes/mod.hpp" - -TUNING_PARAMS tuning; - -static void check_pure_tuning() -{ - // might not be created yet during start up - if(!game.controller) - return; - - if( strcmp(game.controller->gametype, "DM")==0 || - strcmp(game.controller->gametype, "TDM")==0 || - strcmp(game.controller->gametype, "CTF")==0) - { - TUNING_PARAMS p; - if(memcmp(&p, &tuning, sizeof(TUNING_PARAMS)) != 0) - { - dbg_msg("server", "resetting tuning due to pure server"); - tuning = p; - } - } -} - -struct VOTEOPTION -{ - VOTEOPTION *next; - VOTEOPTION *prev; - char command[1]; -}; - -static HEAP *voteoption_heap = 0; -static VOTEOPTION *voteoption_first = 0; -static VOTEOPTION *voteoption_last = 0; - -void send_tuning_params(int cid) -{ - check_pure_tuning(); - - msg_pack_start(NETMSGTYPE_SV_TUNEPARAMS, MSGFLAG_VITAL); - int *params = (int *)&tuning; - for(unsigned i = 0; i < sizeof(tuning)/sizeof(int); i++) - msg_pack_int(params[i]); - msg_pack_end(); - server_send_msg(cid); -} - -// Server hooks -void mods_client_direct_input(int client_id, void *input) -{ - if(!game.world.paused) - game.players[client_id]->on_direct_input((NETOBJ_PLAYER_INPUT *)input); -} - -void mods_client_predicted_input(int client_id, void *input) -{ - if(!game.world.paused) - game.players[client_id]->on_predicted_input((NETOBJ_PLAYER_INPUT *)input); -} - -// Server hooks -void mods_tick() -{ - check_pure_tuning(); - - game.tick(); - -#ifdef CONF_DEBUG - if(config.dbg_dummies) - { - for(int i = 0; i < config.dbg_dummies ; i++) - { - NETOBJ_PLAYER_INPUT input = {0}; - input.direction = (i&1)?-1:1; - game.players[MAX_CLIENTS-i-1]->on_predicted_input(&input); - } - } -#endif -} - -void mods_snap(int client_id) -{ - game.snap(client_id); -} - -void mods_client_enter(int client_id) -{ - //game.world.insert_entity(&game.players[client_id]); - game.players[client_id]->respawn(); - dbg_msg("game", "join player='%d:%s'", client_id, server_clientname(client_id)); - - - char buf[512]; - str_format(buf, sizeof(buf), "%s entered and joined the %s", server_clientname(client_id), game.controller->get_team_name(game.players[client_id]->team)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); - - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), game.players[client_id]->team); -} - -void mods_connected(int client_id) -{ - game.players[client_id] = new(client_id) PLAYER(client_id); - //game.players[client_id].init(client_id); - //game.players[client_id].client_id = client_id; - - // Check which team the player should be on - if(config.sv_tournament_mode) - game.players[client_id]->team = -1; - else - game.players[client_id]->team = game.controller->get_auto_team(client_id); - - (void) game.controller->check_team_balance(); - - // send motd - NETMSG_SV_MOTD msg; - msg.message = config.sv_motd; - msg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); -} - -void mods_client_drop(int client_id) -{ - game.abort_vote_kick_on_disconnect(client_id); - game.players[client_id]->on_disconnect(); - delete game.players[client_id]; - game.players[client_id] = 0; - - (void) game.controller->check_team_balance(); -} - -/*static bool is_separator(char c) { return c == ';' || c == ' ' || c == ',' || c == '\t'; } - -static const char *liststr_find(const char *str, const char *needle) -{ - int needle_len = strlen(needle); - while(*str) - { - int wordlen = 0; - while(str[wordlen] && !is_separator(str[wordlen])) - wordlen++; - - if(wordlen == needle_len && strncmp(str, needle, needle_len) == 0) - return str; - - str += wordlen+1; - } - - return 0; -}*/ - -void mods_message(int msgtype, int client_id) -{ - void *rawmsg = netmsg_secure_unpack(msgtype); - PLAYER *p = game.players[client_id]; - - if(!rawmsg) - { - dbg_msg("server", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on()); - return; - } - - if(msgtype == NETMSGTYPE_CL_SAY) - { - NETMSG_CL_SAY *msg = (NETMSG_CL_SAY *)rawmsg; - int team = msg->team; - if(team) - team = p->team; - else - team = GAMECONTEXT::CHAT_ALL; - - if(config.sv_spamprotection && p->last_chat+time_freq() > time_get()) - return; - - p->last_chat = time_get(); - - game.send_chat(client_id, team, msg->message); - } - else if(msgtype == NETMSGTYPE_CL_CALLVOTE) - { - int64 now = time_get(); - if(game.vote_closetime) - { - game.send_chat_target(client_id, "Wait for current vote to end before calling a new one."); - return; - } - - int64 timeleft = p->last_votecall + time_freq()*60 - now; - if(timeleft > 0) - { - char chatmsg[512] = {0}; - str_format(chatmsg, sizeof(chatmsg), "You must wait %d seconds before making another vote", (timeleft/time_freq())+1); - game.send_chat_target(client_id, chatmsg); - return; - } - - char chatmsg[512] = {0}; - char desc[512] = {0}; - char cmd[512] = {0}; - NETMSG_CL_CALLVOTE *msg = (NETMSG_CL_CALLVOTE *)rawmsg; - if(str_comp_nocase(msg->type, "option") == 0) - { - VOTEOPTION *option = voteoption_first; - while(option) - { - if(str_comp_nocase(msg->value, option->command) == 0) - { - str_format(chatmsg, sizeof(chatmsg), "%s called vote to change server option '%s'", server_clientname(client_id), option->command); - str_format(desc, sizeof(desc), "%s", option->command); - str_format(cmd, sizeof(cmd), "%s", option->command); - break; - } - - option = option->next; - } - - if(!option) - { - str_format(chatmsg, sizeof(chatmsg), "'%s' isn't an option on this server", msg->value); - game.send_chat_target(client_id, chatmsg); - return; - } - } - else if(str_comp_nocase(msg->type, "kick") == 0) - { - if(!config.sv_vote_kick) - { - game.send_chat_target(client_id, "Server does not allow voting to kick players"); - return; - } - - int kick_id = atoi(msg->value); - if(kick_id < 0 || kick_id >= MAX_CLIENTS || !game.players[kick_id]) - { - game.send_chat_target(client_id, "Invalid client id to kick"); - return; - } - - str_format(chatmsg, sizeof(chatmsg), "%s called for vote to kick '%s'", server_clientname(client_id), server_clientname(kick_id)); - str_format(desc, sizeof(desc), "Kick '%s'", server_clientname(kick_id)); - str_format(cmd, sizeof(cmd), "kick %d", kick_id); - if (!config.sv_vote_kick_bantime) - str_format(cmd, sizeof(cmd), "kick %d", kick_id); - else - str_format(cmd, sizeof(cmd), "ban %d %d", kick_id, config.sv_vote_kick_bantime); - } - - if(cmd[0]) - { - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, chatmsg); - game.start_vote(desc, cmd); - p->vote = 1; - game.vote_creator = client_id; - p->last_votecall = now; - game.send_vote_status(-1); - } - } - else if(msgtype == NETMSGTYPE_CL_VOTE) - { - if(!game.vote_closetime) - return; - - if(p->vote == 0) - { - NETMSG_CL_VOTE *msg = (NETMSG_CL_VOTE *)rawmsg; - p->vote = msg->vote; - game.send_vote_status(-1); - } - } - else if (msgtype == NETMSGTYPE_CL_SETTEAM && !game.world.paused) - { - NETMSG_CL_SETTEAM *msg = (NETMSG_CL_SETTEAM *)rawmsg; - - if(config.sv_spamprotection && p->last_setteam+time_freq()*3 > time_get()) - return; - - // Switch team on given client and kill/respawn him - if(game.controller->can_join_team(msg->team, client_id)) - { - if(game.controller->can_change_team(p, msg->team)) - { - p->last_setteam = time_get(); - p->set_team(msg->team); - (void) game.controller->check_team_balance(); - } - else - game.send_broadcast("Teams must be balanced, please join other team", client_id); - } - else - { - char buf[128]; - str_format(buf, sizeof(buf), "Only %d active players are allowed", config.sv_max_clients-config.sv_spectator_slots); - game.send_broadcast(buf, client_id); - } - } - else if (msgtype == NETMSGTYPE_CL_CHANGEINFO || msgtype == NETMSGTYPE_CL_STARTINFO) - { - NETMSG_CL_CHANGEINFO *msg = (NETMSG_CL_CHANGEINFO *)rawmsg; - - if(config.sv_spamprotection && p->last_changeinfo+time_freq()*5 > time_get()) - return; - - p->last_changeinfo = time_get(); - - p->use_custom_color = msg->use_custom_color; - p->color_body = msg->color_body; - p->color_feet = msg->color_feet; - - // check for invalid chars - unsigned char *name = (unsigned char *)msg->name; - while (*name) - { - if(*name < 32) - *name = ' '; - name++; - } - - // copy old name - char oldname[MAX_NAME_LENGTH]; - str_copy(oldname, server_clientname(client_id), MAX_NAME_LENGTH); - - server_setclientname(client_id, msg->name); - if(msgtype == NETMSGTYPE_CL_CHANGEINFO && strcmp(oldname, server_clientname(client_id)) != 0) - { - char chattext[256]; - str_format(chattext, sizeof(chattext), "%s changed name to %s", oldname, server_clientname(client_id)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, chattext); - } - - // set skin - str_copy(p->skin_name, msg->skin, sizeof(p->skin_name)); - - game.controller->on_player_info_change(p); - - if(msgtype == NETMSGTYPE_CL_STARTINFO) - { - // send vote options - NETMSG_SV_VOTE_CLEAROPTIONS clearmsg; - clearmsg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); - VOTEOPTION *current = voteoption_first; - while(current) - { - NETMSG_SV_VOTE_OPTION optionmsg; - optionmsg.command = current->command; - optionmsg.pack(MSGFLAG_VITAL); - server_send_msg(client_id); - current = current->next; - } - - // send tuning parameters to client - send_tuning_params(client_id); - - // - NETMSG_SV_READYTOENTER m; - m.pack(MSGFLAG_VITAL|MSGFLAG_FLUSH); - server_send_msg(client_id); - } - } - else if (msgtype == NETMSGTYPE_CL_EMOTICON && !game.world.paused) - { - NETMSG_CL_EMOTICON *msg = (NETMSG_CL_EMOTICON *)rawmsg; - - if(config.sv_spamprotection && p->last_emote+time_freq()*3 > time_get()) - return; - - p->last_emote = time_get(); - - game.send_emoticon(client_id, msg->emoticon); - } - else if (msgtype == NETMSGTYPE_CL_KILL && !game.world.paused) - { - if(p->last_kill+time_freq()*3 > time_get()) - return; - - p->last_kill = time_get(); - p->kill_character(WEAPON_SELF); - p->respawn_tick = server_tick()+server_tickspeed()*3; - } -} - -static void con_tune_param(void *result, void *user_data) -{ - const char *param_name = console_arg_string(result, 0); - float new_value = console_arg_float(result, 1); - - if(tuning.set(param_name, new_value)) - { - dbg_msg("tuning", "%s changed to %.2f", param_name, new_value); - send_tuning_params(-1); - } - else - console_print("No such tuning parameter"); -} - -static void con_tune_reset(void *result, void *user_data) -{ - TUNING_PARAMS p; - tuning = p; - send_tuning_params(-1); - console_print("tuning reset"); -} - -static void con_tune_dump(void *result, void *user_data) -{ - for(int i = 0; i < tuning.num(); i++) - { - float v; - tuning.get(i, &v); - dbg_msg("tuning", "%s %.2f", tuning.names[i], v); - } -} - - -static void con_change_map(void *result, void *user_data) -{ - game.controller->change_map(console_arg_string(result, 0)); -} - -static void con_restart(void *result, void *user_data) -{ - if(console_arg_num(result)) - game.controller->do_warmup(console_arg_int(result, 0)); - else - game.controller->startround(); -} - -static void con_broadcast(void *result, void *user_data) -{ - game.send_broadcast(console_arg_string(result, 0), -1); -} - -static void con_say(void *result, void *user_data) -{ - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, console_arg_string(result, 0)); -} - -static void con_set_team(void *result, void *user_data) -{ - int client_id = clamp(console_arg_int(result, 0), 0, (int)MAX_CLIENTS); - int team = clamp(console_arg_int(result, 1), -1, 1); - - dbg_msg("", "%d %d", client_id, team); - - if(!game.players[client_id]) - return; - - game.players[client_id]->set_team(team); - (void) game.controller->check_team_balance(); -} - -static void con_addvote(void *result, void *user_data) -{ - int len = strlen(console_arg_string(result, 0)); - - if(!voteoption_heap) - voteoption_heap = memheap_create(); - - VOTEOPTION *option = (VOTEOPTION *)memheap_allocate(voteoption_heap, sizeof(VOTEOPTION) + len); - option->next = 0; - option->prev = voteoption_last; - if(option->prev) - option->prev->next = option; - voteoption_last = option; - if(!voteoption_first) - voteoption_first = option; - - mem_copy(option->command, console_arg_string(result, 0), len+1); - dbg_msg("server", "added option '%s'", option->command); -} - -static void con_vote(void *result, void *user_data) -{ - if(str_comp_nocase(console_arg_string(result, 0), "yes") == 0) - game.vote_enforce = GAMECONTEXT::VOTE_ENFORCE_YES; - else if(str_comp_nocase(console_arg_string(result, 0), "no") == 0) - game.vote_enforce = GAMECONTEXT::VOTE_ENFORCE_NO; - dbg_msg("server", "forcing vote %s", console_arg_string(result, 0)); -} - -void mods_console_init() -{ - MACRO_REGISTER_COMMAND("tune", "si", CFGFLAG_SERVER, con_tune_param, 0, ""); - MACRO_REGISTER_COMMAND("tune_reset", "", CFGFLAG_SERVER, con_tune_reset, 0, ""); - MACRO_REGISTER_COMMAND("tune_dump", "", CFGFLAG_SERVER, con_tune_dump, 0, ""); - - MACRO_REGISTER_COMMAND("change_map", "r", CFGFLAG_SERVER, con_change_map, 0, ""); - MACRO_REGISTER_COMMAND("restart", "?i", CFGFLAG_SERVER, con_restart, 0, ""); - MACRO_REGISTER_COMMAND("broadcast", "r", CFGFLAG_SERVER, con_broadcast, 0, ""); - MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_say, 0, ""); - MACRO_REGISTER_COMMAND("set_team", "ii", CFGFLAG_SERVER, con_set_team, 0, ""); - - MACRO_REGISTER_COMMAND("addvote", "r", CFGFLAG_SERVER, con_addvote, 0, ""); - MACRO_REGISTER_COMMAND("vote", "r", CFGFLAG_SERVER, con_vote, 0, ""); -} - -void mods_init() -{ - //if(!data) /* only load once */ - //data = load_data_from_memory(internal_data); - - for(int i = 0; i < NUM_NETOBJTYPES; i++) - snap_set_staticsize(i, netobj_get_size(i)); - - layers_init(); - col_init(); - - // reset everything here - //world = new GAMEWORLD; - //players = new PLAYER[MAX_CLIENTS]; - - // select gametype - if(strcmp(config.sv_gametype, "mod") == 0) - game.controller = new GAMECONTROLLER_MOD; - else if(strcmp(config.sv_gametype, "ctf") == 0) - game.controller = new GAMECONTROLLER_CTF; - else if(strcmp(config.sv_gametype, "tdm") == 0) - game.controller = new GAMECONTROLLER_TDM; - else - game.controller = new GAMECONTROLLER_DM; - - // setup core world - //for(int i = 0; i < MAX_CLIENTS; i++) - // game.players[i].core.world = &game.world.core; - - // create all entities from the game layer - MAPITEM_LAYER_TILEMAP *tmap = layers_game_layer(); - TILE *tiles = (TILE *)map_get_data(tmap->data); - - /* - num_spawn_points[0] = 0; - num_spawn_points[1] = 0; - num_spawn_points[2] = 0; - */ - - for(int y = 0; y < tmap->height; y++) - { - for(int x = 0; x < tmap->width; x++) - { - int index = tiles[y*tmap->width+x].index; - - if(index >= ENTITY_OFFSET) - { - vec2 pos(x*32.0f+16.0f, y*32.0f+16.0f); - game.controller->on_entity(index-ENTITY_OFFSET, pos); - } - } - } - - //game.world.insert_entity(game.controller); - -#ifdef CONF_DEBUG - if(config.dbg_dummies) - { - for(int i = 0; i < config.dbg_dummies ; i++) - { - mods_connected(MAX_CLIENTS-i-1); - mods_client_enter(MAX_CLIENTS-i-1); - if(game.controller->is_teamplay()) - game.players[MAX_CLIENTS-i-1]->team = i&1; - } - } -#endif -} - -void mods_shutdown() -{ - delete game.controller; - game.controller = 0; - game.clear(); -} - -void mods_presnap() {} -void mods_postsnap() -{ - game.events.clear(); -} - -const char *mods_net_version() { return GAME_NETVERSION; } -const char *mods_version() { return GAME_VERSION; } diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index a0a2b051..0eb61cac 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -1,183 +1,185 @@ #include <new> +#include "player.h" -#include <engine/e_server_interface.h> -#include "player.hpp" -#include "gamecontext.hpp" +MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS) -MACRO_ALLOC_POOL_ID_IMPL(PLAYER, MAX_CLIENTS) - -PLAYER::PLAYER(int client_id) +IServer *CPlayer::Server() const { return m_pGameServer->Server(); } + +CPlayer::CPlayer(CGameContext *pGameServer, int CID, int Team) { - respawn_tick = server_tick(); - character = 0; - this->client_id = client_id; + m_pGameServer = pGameServer; + m_RespawnTick = Server()->Tick(); + m_DieTick = Server()->Tick(); + Character = 0; + this->m_ClientID = CID; + m_Team = GameServer()->m_pController->ClampTeam(Team); } -PLAYER::~PLAYER() +CPlayer::~CPlayer() { - delete character; - character = 0; + delete Character; + Character = 0; } -void PLAYER::tick() +void CPlayer::Tick() { - server_setclientscore(client_id, score); + Server()->SetClientScore(m_ClientID, m_Score); // do latency stuff { - CLIENT_INFO info; - if(server_getclientinfo(client_id, &info)) + IServer::CClientInfo Info; + if(Server()->GetClientInfo(m_ClientID, &Info)) { - latency.accum += info.latency; - latency.accum_max = max(latency.accum_max, info.latency); - latency.accum_min = min(latency.accum_min, info.latency); + m_Latency.m_Accum += Info.m_Latency; + m_Latency.m_AccumMax = max(m_Latency.m_AccumMax, Info.m_Latency); + m_Latency.m_AccumMin = min(m_Latency.m_AccumMin, Info.m_Latency); } - - if(server_tick()%server_tickspeed() == 0) + // each second + if(Server()->Tick()%Server()->TickSpeed() == 0) { - latency.avg = latency.accum/server_tickspeed(); - latency.max = latency.accum_max; - latency.min = latency.accum_min; - latency.accum = 0; - latency.accum_min = 1000; - latency.accum_max = 0; + m_Latency.m_Avg = m_Latency.m_Accum/Server()->TickSpeed(); + m_Latency.m_Max = m_Latency.m_AccumMax; + m_Latency.m_Min = m_Latency.m_AccumMin; + m_Latency.m_Accum = 0; + m_Latency.m_AccumMin = 1000; + m_Latency.m_AccumMax = 0; } } - if(!character && die_tick+server_tickspeed()*3 <= server_tick()) - spawning = true; + if(!Character && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick()) + m_Spawning = true; - if(character) + if(Character) { - if(character->alive) + if(Character->IsAlive()) { - view_pos = character->pos; + m_ViewPos = Character->m_Pos; } else { - delete character; - character = 0; + delete Character; + Character = 0; } } - else if(spawning && respawn_tick <= server_tick()) - try_respawn(); + else if(m_Spawning && m_RespawnTick <= Server()->Tick()) + TryRespawn(); } -void PLAYER::snap(int snapping_client) +void CPlayer::Snap(int SnappingClient) { - NETOBJ_CLIENT_INFO *client_info = (NETOBJ_CLIENT_INFO *)snap_new_item(NETOBJTYPE_CLIENT_INFO, client_id, sizeof(NETOBJ_CLIENT_INFO)); - str_to_ints(&client_info->name0, 6, server_clientname(client_id)); - str_to_ints(&client_info->skin0, 6, skin_name); - client_info->use_custom_color = use_custom_color; - client_info->color_body = color_body; - client_info->color_feet = color_feet; - - NETOBJ_PLAYER_INFO *info = (NETOBJ_PLAYER_INFO *)snap_new_item(NETOBJTYPE_PLAYER_INFO, client_id, sizeof(NETOBJ_PLAYER_INFO)); - - info->latency = latency.min; - info->latency_flux = latency.max-latency.min; - info->local = 0; - info->cid = client_id; - info->score = score; - info->team = team; - - if(client_id == snapping_client) - info->local = 1; + CNetObj_ClientInfo *ClientInfo = static_cast<CNetObj_ClientInfo *>(Server()->SnapNewItem(NETOBJTYPE_CLIENTINFO, m_ClientID, sizeof(CNetObj_ClientInfo))); + StrToInts(&ClientInfo->m_Name0, 6, Server()->ClientName(m_ClientID)); + StrToInts(&ClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); + ClientInfo->m_UseCustomColor = m_TeeInfos.m_UseCustomColor; + ClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; + ClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + + CNetObj_PlayerInfo *Info = static_cast<CNetObj_PlayerInfo *>(Server()->SnapNewItem(NETOBJTYPE_PLAYERINFO, m_ClientID, sizeof(CNetObj_PlayerInfo))); + + Info->m_Latency = m_Latency.m_Min; + Info->m_LatencyFlux = m_Latency.m_Max-m_Latency.m_Min; + Info->m_Local = 0; + Info->m_ClientId = m_ClientID; + Info->m_Score = m_Score; + Info->m_Team = m_Team; + + if(m_ClientID == SnappingClient) + Info->m_Local = 1; } -void PLAYER::on_disconnect() +void CPlayer::OnDisconnect() { - kill_character(WEAPON_GAME); - - //game.controller->on_player_death(&game.players[client_id], 0, -1); - - char buf[512]; - str_format(buf, sizeof(buf), "%s has left the game", server_clientname(client_id)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); + KillCharacter(); + + if(Server()->ClientIngame(m_ClientID)) + { + char Buf[512]; + str_format(Buf, sizeof(Buf), "%s has left the game", Server()->ClientName(m_ClientID)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Buf); - dbg_msg("game", "leave player='%d:%s'", client_id, server_clientname(client_id)); + dbg_msg("game", "leave player='%d:%s'", m_ClientID, Server()->ClientName(m_ClientID)); + } } -void PLAYER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input) +void CPlayer::OnPredictedInput(CNetObj_PlayerInput *NewInput) { - CHARACTER *chr = get_character(); - if(chr) - chr->on_predicted_input(new_input); + if(Character) + Character->OnPredictedInput(NewInput); } -void PLAYER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input) +void CPlayer::OnDirectInput(CNetObj_PlayerInput *NewInput) { - CHARACTER *chr = get_character(); - if(chr) - chr->on_direct_input(new_input); + if(Character) + Character->OnDirectInput(NewInput); - if(!chr && team >= 0 && (new_input->fire&1)) - spawning = true; + if(!Character && m_Team >= 0 && (NewInput->m_Fire&1)) + m_Spawning = true; - if(!chr && team == -1) - view_pos = vec2(new_input->target_x, new_input->target_y); + if(!Character && m_Team == -1) + m_ViewPos = vec2(NewInput->m_TargetX, NewInput->m_TargetY); } -CHARACTER *PLAYER::get_character() +CCharacter *CPlayer::GetCharacter() { - if(character && character->alive) - return character; + if(Character && Character->IsAlive()) + return Character; return 0; } -void PLAYER::kill_character(int weapon) +void CPlayer::KillCharacter(int Weapon) { - //CHARACTER *chr = get_character(); - if(character) + if(Character) { - character->die(client_id, weapon); - delete character; - character = 0; + Character->Die(m_ClientID, Weapon); + delete Character; + Character = 0; } } -void PLAYER::respawn() +void CPlayer::Respawn() { - if(team > -1) - spawning = true; + if(m_Team > -1) + m_Spawning = true; } -void PLAYER::set_team(int new_team) +void CPlayer::SetTeam(int Team) { // clamp the team - new_team = game.controller->clampteam(new_team); - if(team == new_team) + Team = GameServer()->m_pController->ClampTeam(Team); + if(m_Team == Team) return; - char buf[512]; - str_format(buf, sizeof(buf), "%s joined the %s", server_clientname(client_id), game.controller->get_team_name(new_team)); - game.send_chat(-1, GAMECONTEXT::CHAT_ALL, buf); + char Buf[512]; + str_format(Buf, sizeof(Buf), "%s joined the %s", Server()->ClientName(m_ClientID), GameServer()->m_pController->GetTeamName(Team)); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Buf); - kill_character(WEAPON_GAME); - team = new_team; - score = 0; - dbg_msg("game", "team_join player='%d:%s' team=%d", client_id, server_clientname(client_id), team); + KillCharacter(); + m_Team = Team; + m_Score = 0; + // we got to wait 0.5 secs before respawning + m_RespawnTick = Server()->Tick()+Server()->TickSpeed()/2; + dbg_msg("game", "team_join player='%d:%s' m_Team=%d", m_ClientID, Server()->ClientName(m_ClientID), m_Team); - game.controller->on_player_info_change(game.players[client_id]); + GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]); } -void PLAYER::try_respawn() +void CPlayer::TryRespawn() { - vec2 spawnpos = vec2(100.0f, -60.0f); + vec2 SpawnPos = vec2(100.0f, -60.0f); - if(!game.controller->can_spawn(this, &spawnpos)) + if(!GameServer()->m_pController->CanSpawn(this, &SpawnPos)) return; // check if the position is occupado - ENTITY *ents[2] = {0}; - int num_ents = game.world.find_entities(spawnpos, 64, ents, 2, NETOBJTYPE_CHARACTER); + CEntity *apEnts[2] = {0}; + int NumEnts = GameServer()->m_World.FindEntities(SpawnPos, 64, apEnts, 2, NETOBJTYPE_CHARACTER); - if(num_ents == 0) + if(NumEnts == 0) { - spawning = false; - character = new(client_id) CHARACTER(); - character->spawn(this, spawnpos, team); - game.create_playerspawn(spawnpos); + m_Spawning = false; + Character = new(m_ClientID) CCharacter(&GameServer()->m_World); + Character->Spawn(this, SpawnPos); + GameServer()->CreatePlayerSpawn(SpawnPos); } } diff --git a/src/game/server/player.h b/src/game/server/player.h new file mode 100644 index 00000000..1b631d45 --- /dev/null +++ b/src/game/server/player.h @@ -0,0 +1,89 @@ +#ifndef GAME_SERVER_PLAYER_H +#define GAME_SERVER_PLAYER_H + +// this include should perhaps be removed +#include "entities/character.h" +#include "gamecontext.h" + +// player object +class CPlayer +{ + MACRO_ALLOC_POOL_ID() + +public: + CPlayer(CGameContext *pGameServer, int CID, int Team); + ~CPlayer(); + + void Init(int CID); + + void TryRespawn(); + void Respawn(); + void SetTeam(int Team); + int GetTeam() const { return m_Team; }; + int GetCID() const { return m_ClientID; }; + + void Tick(); + void Snap(int SnappingClient); + + void OnDirectInput(CNetObj_PlayerInput *NewInput); + void OnPredictedInput(CNetObj_PlayerInput *NewInput); + void OnDisconnect(); + + void KillCharacter(int Weapon = WEAPON_GAME); + CCharacter *GetCharacter(); + + //--------------------------------------------------------- + // this is used for snapping so we know how we can clip the view for the player + vec2 m_ViewPos; + + // + int m_Vote; + int m_VotePos; + // + int m_Last_VoteCall; + int m_Last_VoteTry; + int m_Last_Chat; + int m_Last_SetTeam; + int m_Last_ChangeInfo; + int m_Last_Emote; + int m_Last_Kill; + + // TODO: clean this up + struct + { + char m_SkinName[64]; + int m_UseCustomColor; + int m_ColorBody; + int m_ColorFeet; + } m_TeeInfos; + + int m_RespawnTick; + int m_DieTick; + int m_Score; + bool m_ForceBalanced; + +private: + CCharacter *Character; + CGameContext *m_pGameServer; + + CGameContext *GameServer() const { return m_pGameServer; } + IServer *Server() const; + + // + bool m_Spawning; + int m_ClientID; + int m_Team; + + // network latency calculations + struct + { + int m_Accum; + int m_AccumMin; + int m_AccumMax; + int m_Avg; + int m_Min; + int m_Max; + } m_Latency; +}; + +#endif diff --git a/src/game/server/player.hpp b/src/game/server/player.hpp deleted file mode 100644 index e93aee01..00000000 --- a/src/game/server/player.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef GAME_SERVER_PLAYER_H -#define GAME_SERVER_PLAYER_H - -// this include should perhaps be removed -#include "entities/character.hpp" - -// player object -class PLAYER -{ - MACRO_ALLOC_POOL_ID() -private: - CHARACTER *character; -public: - PLAYER(int client_id); - ~PLAYER(); - - // TODO: clean this up - char skin_name[64]; - int use_custom_color; - int color_body; - int color_feet; - - int respawn_tick; - int die_tick; - // - bool spawning; - int client_id; - int team; - int score; - bool force_balanced; - - // - int vote; - int64 last_votecall; - - // - int64 last_chat; - int64 last_setteam; - int64 last_changeinfo; - int64 last_emote; - int64 last_kill; - - // network latency calculations - struct - { - int accum; - int accum_min; - int accum_max; - int avg; - int min; - int max; - } latency; - - // this is used for snapping so we know how we can clip the view for the player - vec2 view_pos; - - void init(int client_id); - - CHARACTER *get_character(); - - void kill_character(int weapon); - - void try_respawn(); - void respawn(); - void set_team(int team); - - void tick(); - void snap(int snapping_client); - - void on_direct_input(NETOBJ_PLAYER_INPUT *new_input); - void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input); - void on_disconnect(); -}; - -#endif |