void BuildableComponent::HandlePrepareNetCode() { if (state == CONSTRUCTING) { entity.oldEnt->s.eFlags &= ~EF_B_SPAWNED; } else { entity.oldEnt->s.eFlags |= EF_B_SPAWNED; } if (state == POST_BLAST) { entity.oldEnt->s.eFlags |= EF_NODRAW; } else { entity.oldEnt->s.eFlags &= ~EF_NODRAW; } if (marked) { entity.oldEnt->s.eFlags |= EF_B_MARKED; } else { entity.oldEnt->s.eFlags &= ~EF_B_MARKED; } if (Powered()) { entity.oldEnt->s.eFlags |= EF_B_POWERED; } else { entity.oldEnt->s.eFlags &= ~EF_B_POWERED; } if (GetHealthComponent().Alive()) { entity.oldEnt->s.eFlags &= ~EF_DEAD; } else { entity.oldEnt->s.eFlags |= EF_DEAD; entity.oldEnt->s.eFlags &= ~EF_FIRING; } }
void BuildableComponent::HandleDie(gentity_t* killer, meansOfDeath_t meansOfDeath) { // Note that this->state is adjusted in (Alien|Human)BuildableComponent::HandleDie so they have // access to its current value. TeamComponent::team_t team = GetTeamComponent().Team(); // TODO: Move animation code to BuildableComponent. G_SetBuildableAnim(entity.oldEnt, Powered() ? BANIM_DESTROY : BANIM_DESTROY_UNPOWERED, true); G_SetIdleBuildableAnim(entity.oldEnt, BANIM_DESTROYED); entity.oldEnt->killedBy = killer->s.number; G_LogDestruction(entity.oldEnt, killer, meansOfDeath); // TODO: Handle in TaggableComponent. Beacon::DetachTags(entity.oldEnt); // Report an attack to the defending team if the buildable was powered and there is a main // buildable that can report it. Note that the main buildables itself issues its own warnings. if (Powered() && entity.Get<MainBuildableComponent>() == nullptr && G_IsWarnableMOD(meansOfDeath) && G_ActiveMainBuildable(team)) { // Get a nearby location entity. gentity_t *location = GetCloseLocationEntity(entity.oldEnt); // Fall back to fake location entity if necessary. if (!location) location = level.fakeLocation; // Warn if there was no warning for this location recently. if (level.time > location->warnTimer) { bool inBase = G_InsideBase(entity.oldEnt); G_BroadcastEvent(EV_WARN_ATTACK, inBase ? 0 : location->s.number, team); Beacon::NewArea(BCT_DEFEND, entity.oldEnt->s.origin, team); location->warnTimer = level.time + ATTACKWARN_NEARBY_PERIOD; } } // If not deconstructed, add all build points to queue. if (meansOfDeath != MOD_DECONSTRUCT && meansOfDeath != MOD_REPLACE) { G_FreeBudget(team, 0, BG_Buildable(entity.oldEnt->s.modelindex)->buildPoints); } }
void CBuilding::Tick() { // handle death-tiles and leaving gamelayer if(GameServer()->Collision()->GetCollisionAt(m_Pos.x+m_ProximityRadius/2.f, m_Pos.y-m_ProximityRadius/2.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetCollisionAt(m_Pos.x+m_ProximityRadius/2.f, m_Pos.y+m_ProximityRadius/2.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetCollisionAt(m_Pos.x-m_ProximityRadius/2.f, m_Pos.y-m_ProximityRadius/2.f)&CCollision::COLFLAG_DEATH || GameServer()->Collision()->GetCollisionAt(m_Pos.x-m_ProximityRadius/2.f, m_Pos.y+m_ProximityRadius/2.f)&CCollision::COLFLAG_DEATH || GameLayerClipped(m_Pos)) { Die(-1, WEAPON_WORLD); return; } if(!IsGrounded()) { Die(-1, WEAPON_WORLD); return; } if(Server()->Tick() % 5 == 0 && m_Decon) { if( ((CGameControllerNODES*)GameServer()->m_pController)->Spawns[m_Team] <= 0) { m_Decon = false; } else { TakeDamage(vec2(0,0), 1, -1, WEAPON_SELF); } } if(m_Decay > 0) m_Decay--; if(m_AnimDecay > 0) m_AnimDecay--; if(m_SpawnanimDecay > 0) m_SpawnanimDecay--; m_Power = Powered(); if(!m_Power && (m_Type == B_REPEATER || m_Type == B_TELEPORT)) m_Anim = 0; if(!m_Power) m_Hit = 0; if(!m_Power && m_Type == B_TELEPORT) m_Anim = 0; if(!m_Alive || (!m_Power && m_Type != B_SHIELD)) return; if(Server()->Tick() % 100 == 0 && m_Health < m_Maxhealth/4) { GameServer()->CreateExplosion(pos, -1, -1, true); } switch(m_Type) { case B_REACTOR: { if(m_AnimDecay == 0) { m_Anim++; if(m_Anim > 3) { m_Anim = 0; m_AnimDecay = 100; } else m_AnimDecay = 10; } } break; case B_REPEATER: { if(!m_Power) { m_Anim = 0; } else { if(m_AnimDecay == 0) { m_Anim++; if(m_Anim > 1) { m_Anim = 0; m_AnimDecay = 100; } else m_AnimDecay = 10; } } } break; case B_SPAWN: { m_Anim = m_Anim & 0xF; if(m_AnimDecay == 0) { m_Anim++; if(m_Anim > 6) m_Anim = 0; m_AnimDecay = 35; } if(m_Spawnanim > 0 && m_SpawnanimDecay == 0) { m_Spawnanim--; m_SpawnanimDecay = 4; } m_Anim = (m_Spawnanim << 4) | m_Anim; } break; case B_AMMO1: case B_AMMO2: case B_AMMO3: { CCharacter *c = GetNearest(ms_PhysSize, m_Team); if(c) { if(m_Anim < 3 && m_AnimDecay == 0) { m_Anim++; m_AnimDecay = 4; } if(m_Decay == 0 && m_Anim == 3) { m_Decay = 50; int weapon = -1; if(m_Type == B_AMMO1) weapon = WEAPON_SHOTGUN; else if(m_Type == B_AMMO2) weapon = WEAPON_GRENADE; else if(m_Type == B_AMMO3) weapon = WEAPON_RIFLE; if(!c->m_Weapons[weapon].m_Got) { c->m_Weapons[weapon].m_Got = 1; c->m_ActiveWeapon = weapon; GameServer()->CreateSound(m_Pos, SOUND_PICKUP_SHOTGUN); } else { GameServer()->CreateSound(m_Pos, SOUND_PICKUP_GRENADE); } c->m_Weapons[weapon].m_Ammo = 10; } } else { if(m_Anim > 0 && m_AnimDecay == 0) { m_Anim--; m_AnimDecay = 4; } } } break; case B_TURRET1: { vec2 realpos = vec2(m_Pos.x, m_Pos.y-74); vec2 tmppos = m_Pos; m_Pos = realpos; CCharacter *c = GetNearest(700, m_Team^1); m_Pos = tmppos; if(c) { m_Angle = GetAngle(normalize(c->m_Pos - realpos)); if(m_Decay == 0) { m_Decay = 12; float a = m_Angle; a+=frandom()*0.20f -0.10f; CProjectile *pProj = new CProjectile(WEAPON_GUN, -1, realpos, vec2(cosf(a), sinf(a)), (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_GunLifetime), 1, 0, 0, -1, WEAPON_GUN, false); GameServer()->CreateSound(realpos, SOUND_GUN_FIRE); } } } break; case B_TURRET2: { vec2 realpos = vec2(m_Pos.x, m_Pos.y-66); vec2 tmppos = m_Pos; m_Pos = realpos; CCharacter *c = GetNearest(500, m_Team^1); m_Pos = tmppos; if(c) { m_Angle = GetAngle(normalize(c->m_Pos-realpos)); if(m_Decay == 0) { m_Decay = 30; int shotspread = 2; for(int i = -shotspread; i <= shotspread; i++) { float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; float a = m_Angle; a += spreading[i+2]; float v = 1-(abs(i)/(float)shotspread); float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v); CProjectile *pProj = new CProjectile(WEAPON_SHOTGUN, -2, realpos, vec2(cosf(a), sinf(a))*speed, (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_ShotgunLifetime), 1, 0, 0, -1, WEAPON_SHOTGUN, false); } game.create_sound(realpos, SOUND_SHOTGUN_FIRE); } } } break; case B_HEALTH: case B_ARMOR: { CCharacter *c = GetNearest(ms_PhysSize, m_Team); if(c) { if(m_Decay == 0) { //wait for open first if(m_AnimDecay == 0 && m_Decay == 0 && m_Anim < 4) { m_AnimDecay = 3; m_Anim++; } else if(m_AnimDecay == 0) { if(m_Type == B_HEALTH && c->m_Health < 10) { m_Decay = 150; c->IncreaseHealth(3); GameServer()->CreateSound(m_Pos, SOUND_PICKUP_HEALTH); c->m_Ill = false; } else if(m_Type == B_HEALTH) { c->m_Ill = false; } else if(m_Type == B_ARMOR && c->m_Armor < 10) { m_Decay = 150; c->IncreaseArmor(3); GameServer()->CreateSound(m_Pos, SOUND_PICKUP_ARMOR); } } } } else { if(m_AnimDecay == 0 && m_Decay == 0 && m_Anim > 0) { m_AnimDecay = 3; m_Anim--; } } } break; case B_SHIELD: { if(Server()->Tick() % 50 == 0 && m_Power) { if(m_Armor < 30) m_Armor++; //game.create_damageind(pos, 3.141592, armor > 0 ? (int)(10.0f*armor/30) : 0); } if(Server()->Tick() % 5 == 0) { if(m_Hit > 0) m_Hit--; if(m_Power && m_Armor > 0) { CProjectile *pProj[128]; int num = GameServer()->m_World.FindEntities(m_Pos, 250.0f, (CEntity**)pProj, 128, CGameWorld::ENTTYPE_PROJECTILE); for(int i = 0; i < num && m_Armor > 0; i++) { if(pProj[i]->m_Bounce) continue; if((pProj[i]->m_Owner >= 0) && GameServer()->m_apPlayers[pProj[i]->m_Owner] && GameServer()->m_apPlayers[pProj[i]->m_Owner]->m_Team == m_Team) continue; if(pProj[i]->m_Owner < 0) continue; pProj[i]->m_Initpos = pProj[i]->m_Pos; pProj[i]->m_StartTick = Server()->Tick(); pProj[i]->m_Direction = normalize(pProj[i]->m_Pos-m_Pos); pProj[i]->m_Bounce = 1; m_Hit = 3; //game.create_shieldhit(m_Pos, 0); m_Armor = clamp(m_Armor-(pProj[i]->m_Weapon == WEAPON_GRENADE ? 4 : 2), 0, 30); if(m_Armor == 0) { m_Armor = -4; } } } } m_Anim = (m_Armor > 0 ? (int)(9.0f*m_Armor/30.0f) : 0) | (m_Hit << 4); } break; //NODESTODO: COUNTINUE! case B_TELEPORT: { if(m_Decay == 0) { //find other teleport... D= CBuilding *other = 0; for(int i = 0; i < ((CGameControllerNODES*)GameServer()->m_pController)->BuildingsCount[m_Team]; i++) { CBuilding *b = ((CGameControllerNODES*)GameServer()->m_pController)->Buildings[m_Team][i]; if( b != this && b->m_Type == B_TELEPORT && b->m_Alive && b->m_Power) { other = b; } } if(other) { bool is_enemy = false; CCharacter *c = GetNearest(ms_PhysSize-4.0f, m_Team); if(!c && m_Anim == 8) {//once opened, port enemies as well c = GetNearest(ms_PhysSize-4.0f, m_Team^1); is_enemy = true; } if(c) { if(m_Anim < 8 && m_AnimDecay == 0 && !is_enemy) { m_Anim++; m_AnimDecay = 2; other->m_Anim = m_Anim; other->m_AnimDecay = m_AnimDecay+1; other->m_Decay =m_AnimDecay+1; } else if(anim == 8 && m_AnimDecay == 0) { if(!c->m_pPlayer->m_Ported) { //dbg_msg("nodes", "yay"); float ill_prob = min((distance(other->m_Pos, m_Pos))/8000.0f * 0.2f, 0.2f); for(int i = 0; i < MAX_CLIENTS; i++) { if(game.players[i] && game.players[i]->get_character() && (game.players[i]->get_character()->core.hooked_player == c->player->client_id || c->core.hooked_player == game.players[i]->client_id)) { CCharacter *hc = game.players[i]->get_character(); hc->m_Pos = vec2(other->m_Pos.x, other->m_Pos.y-1); hc->core.m_Pos = vec2(other->m_Pos.x, other->m_Pos.y-1); hc->player->ported = true; hc->core.hook_state = -1; hc->core.hooked_player = -1; if(frandom() <= ill_prob) { if(!hc->ill) { hc->ill = true; hc->infecter = -1; } } } } //dbg_msg("nodes", "porting %d from (%d,%d) to (%d,%d)", c->player->client_id, (int)pos.x, (int)pos.y, (int)other->pos.x, (int)other->pos.y); c->pos = vec2(other->pos.x, other->pos.y-1); c->core.pos = vec2(other->pos.x, other->pos.y-1); c->player->ported = true; c->core.hook_state = -1; c->core.hooked_player = -1; if(frandom() <= ill_prob) { if(!c->ill) { c->ill = true; c->infecter = -1; } } decay = 50; other->decay = 50; anim = 1; other->anim = 1; game.create_playerspawn(other->pos); game.create_sound(pos, SOUND_TELEPORT); game.create_death(pos, c->player->client_id); game.create_sound(other->pos, SOUND_TELEPORT); } else { anim = 0; } } } else { if(anim > 0 && anim_decay == 0) { anim++; if(anim == 9) anim = 0; anim_decay = 2; other->anim = anim; other->anim_decay = anim_decay+1; other->decay =anim_decay+1; } } } } else { if(anim_decay == 0 && anim > 0) { anim_decay = 2; anim++; if(anim == 9) anim = 0; } } } break; } return; }
void BuildableComponent::Think(int timeDelta) { lifecycle_t oldState; do { switch ((oldState = state)) { case CONSTRUCTING: { int constructionTime = BG_Buildable(entity.oldEnt->s.modelindex)->buildTime; if (entity.oldEnt->creationTime + constructionTime < level.time) { // Finish construction. state = CONSTRUCTED; // Award momentum. G_AddMomentumForBuilding(entity.oldEnt); } else { // Gain health while constructing. entity.Heal(GetHealthComponent().MaxHealth() * ((float)timeDelta / (float)constructionTime) * (1.0f - BUILDABLE_START_HEALTH_FRAC), nullptr); } } break; case CONSTRUCTED: { // Set legacy gentity_t fields. entity.oldEnt->spawned = true; entity.oldEnt->enabled = true; if (Powered()) { // Regenerate health. int regenWait; float regenRate = (float)BG_Buildable(entity.oldEnt->s.modelindex)->regenRate; switch (entity.oldEnt->buildableTeam) { case TEAM_ALIENS: regenWait = ALIEN_BUILDABLE_REGEN_WAIT; break; case TEAM_HUMANS: regenWait = HUMAN_BUILDABLE_REGEN_WAIT; break; default: regenWait = 0; break; } if (regenRate && (entity.oldEnt->lastDamageTime + regenWait) < level.time) { entity.Heal(timeDelta * 0.001f * regenRate, nullptr); } } // Notify other components when construction finishes. if (!constructionHasFinished) { entity.FinishConstruction(); constructionHasFinished = true; } } break; default: // All other states are death states. return; } } while (state != oldState); // After every state change, the new state is evaluated, too. // TODO: Move this to SpawnerComponent? entity.oldEnt->clientSpawnTime = std::max(0, entity.oldEnt->clientSpawnTime - timeDelta); // Check if touching any triggers. // TODO: Move helper here. G_BuildableTouchTriggers(entity.oldEnt); }