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);
	}
}
Example #3
0
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);
}