Exemple #1
0
void C4FacetEx::DrawBolt(int iX1, int iY1, int iX2, int iY2, BYTE bCol,
                         BYTE bCol2) {
  if (!lpDDraw || !Surface || !Wdt || !Hgt) return;
  // Scroll position
  iX1 -= TargetX;
  iY1 -= TargetY;
  iX2 -= TargetX;
  iY2 -= TargetY;
  // Facet bounds
  if (!Inside(iX1, 0, Wdt - 1) && !Inside(iX2, 0, Wdt - 1)) return;
  if (!Inside(iY1, 0, Hgt - 1) && !Inside(iY2, 0, Hgt - 1)) return;
  iX1 += X;
  iX2 += X;
  iY1 += Y;
  iY2 += Y;
  // Draw bolt
  int pvtx[2 * 4];
  pvtx[0] = iX1;
  pvtx[1] = iY1;
  pvtx[2] = iX2;
  pvtx[3] = iY2;
#ifdef C4ENGINE
  pvtx[4] = iX2 + SafeRandom(DrawBoltR1) - DrawBoltR2;
  pvtx[5] = iY2 + SafeRandom(DrawBoltR1) - DrawBoltR2;
  pvtx[6] = iX1 + SafeRandom(DrawBoltR1) - DrawBoltR2;
  pvtx[7] = iY1 + SafeRandom(DrawBoltR1) - DrawBoltR2;
#else
  pvtx[4] = iX2 + X % 3 - 1;
  pvtx[5] = iY2 + X % 3 - 1;
  pvtx[6] = iX1 + Y % 3 - 1;
  pvtx[7] = iY1 + Y % 3 - 1;
#endif
  // Draw in surface
  DWORD dwClr1 = lpDDraw->Pal.GetClr(bCol), dwClr2;
  DWORD dwClr3 = lpDDraw->Pal.GetClr(bCol2), dwClr4;
  /*if (DDrawCfg.NoBoxFades)
          {*/
  dwClr2 = dwClr1;
  dwClr4 = dwClr3;
  /*}
else
  {
  DWORD dwClr2=dwClr1|0xff000000; ...this leads to black and white lightning
bolts. Who wants that?
  DWORD dwClr4=dwClr3|0xff000000;
  }*/
  lpDDraw->DrawQuadDw(Surface, pvtx, dwClr1, dwClr3, dwClr4, dwClr2);
}
Exemple #2
0
C4SoundEffect *C4SoundSystem::GetEffect(const char *szSndName) {
  C4SoundEffect *pSfx;
  char szName[C4MaxSoundName + 4 + 1];
  int32_t iNumber;
  // Evaluate sound name
  SCopy(szSndName, szName, C4MaxSoundName);
  // Default extension
  DefaultExtension(szName, "wav");
  // Convert old style '*' wildcard to correct '?' wildcard
  // For sound effects, '*' is supposed to match single digits only
  SReplaceChar(szName, '*', '?');
  // Sound with a wildcard: determine number of available matches
  if (SCharCount('?', szName)) {
    // Search global sound file
    if (!(iNumber = SoundFile.EntryCount(szName)))
      // Search scenario local files
      if (!(iNumber = Game.ScenarioFile.EntryCount(szName)))
        // Search bank loaded sounds
        if (!(iNumber = EffectInBank(szName)))
          // None found: failure
          return NULL;
    // Insert index to name
    iNumber = BoundBy(1 + SafeRandom(iNumber), 1, 9);
    SReplaceChar(szName, '?', '0' + iNumber);
  }
  // Find requested sound effect in bank
  for (pSfx = FirstSound; pSfx; pSfx = pSfx->Next)
    if (SEqualNoCase(szName, pSfx->Name)) break;
  // Sound not in bank, try add
  if (!pSfx)
    if (!(pSfx = AddEffect(szName))) return NULL;
  return pSfx;
}
StdStrBuf C4TeamList::GetScriptPlayerName() const
{
	// get a name to assign to a new script player. Try to avoid name conflicts
	if (!sScriptPlayerNames.getLength()) return StdStrBuf(LoadResStr("IDS_TEXT_COMPUTER")); // default name
	// test available script names
	int32_t iNameIdx = 0; StdStrBuf sOut;
	while (sScriptPlayerNames.GetSection(iNameIdx++, &sOut, '|'))
		if (!Game.PlayerInfos.GetActivePlayerInfoByName(sOut.getData()))
			return sOut;
	// none are available: Return a random name
	sScriptPlayerNames.GetSection(SafeRandom(iNameIdx-1), &sOut, '|');
	return sOut;
}
bool C4MusicSystem::ScheduleWaitTime()
{
	// Roll for scheduling a break after the next piece.
	if (SCounter < 3) return false; // But not right away.
	if (SafeRandom(100) >= music_break_chance) return false;
	if (music_break_max > 0)
	{
		int32_t music_break = music_break_min;
		if (music_break_max > music_break_min) music_break += SafeRandom(music_break_max - music_break_min); // note that SafeRandom has limited range
		if (music_break > 0)
		{
			is_waiting = true;
			wait_time_end = C4TimeMilliseconds::Now() + music_break;
			if (::Config.Sound.Verbose)
			{
				LogF("MusicSystem: Pause (%d msecs)", (int)music_break);
			}
			// After wait, do not resume previously started songs
			for (C4MusicFile *check_file = Songs; check_file; check_file = check_file->pNext)
				check_file->ClearResumePos();
		}
	}
	return is_waiting;
}
void C4Network2Res::StartNewLoads()
{
	if (!pCChunks) return;
	// count clients
	int32_t iCChunkCnt = 0; ClientChunks *pChunks;
	for (pChunks = pCChunks; pChunks; pChunks = pChunks->Next)
		iCChunkCnt++;
	// create array
	ClientChunks **pC = new ClientChunks *[iCChunkCnt];
	// initialize
	int32_t i;
	for (i = 0; i < iCChunkCnt; i++) pC[i] = NULL;
	// create shuffled order
	for (pChunks = pCChunks, i = 0; i < iCChunkCnt; i++, pChunks = pChunks->Next)
	{
		// determine position
		int32_t iPos = SafeRandom(iCChunkCnt - i);
		// find & set
		for (int32_t j = 0; ; j++)
			if (!pC[j] && !iPos--)
			{
				pC[j] = pChunks;
				break;
			}
	}
	// start new load until maximum count reached
	while (iLoadCnt + 1 <= C4NetResMaxLoad)
	{
		int32_t ioLoadCnt = iLoadCnt;
		// search someone
		for (i = 0; i < iCChunkCnt; i++)
			if (pC[i])
			{
				// try to start load
				if (!StartLoad(pC[i]->ClientID, pC[i]->Chunks))
					{ RemoveCChunks(pC[i]); pC[i] = NULL; continue; }
				// success?
				if (iLoadCnt > ioLoadCnt) break;
			}
		// not found?
		if (i >= iCChunkCnt)
			break;
	}
	// clear up
	delete [] pC;
}
C4Team *C4TeamList::GetRandomSmallestTeam() const
{
	C4Team *pLowestTeam = NULL; int iLowestTeamCount = 0;
	C4Team **ppCheck=ppList; int32_t iCnt=iTeamCount;
	for (; iCnt--; ++ppCheck)
	{
		if ((*ppCheck)->IsFull()) continue; // do not join into full teams
		if (!pLowestTeam || pLowestTeam->GetPlayerCount() > (*ppCheck)->GetPlayerCount())
		{
			pLowestTeam = *ppCheck;
			iLowestTeamCount = 1;
		}
		else if (pLowestTeam->GetPlayerCount() == (*ppCheck)->GetPlayerCount())
			if (!SafeRandom(++iLowestTeamCount))
				pLowestTeam = *ppCheck;
	}
	return pLowestTeam;
}
int C4LoaderScreen::SeekLoaderScreens(C4Group &rFromGrp, const char *szWildcard, int iLoaderCount, char *szDstName, C4Group **ppDestGrp)
	{
	BOOL fFound;
	int iLocalLoaders=0;
	char Filename[_MAX_PATH+1];
	for (fFound=rFromGrp.FindEntry(szWildcard, Filename); fFound; fFound=rFromGrp.FindNextEntry(szWildcard, Filename))
		{
		// loader found; choose it, if Daniel wants it that way
		++iLocalLoaders;
		if (!SafeRandom(++iLoaderCount))
			{
			// copy group and path
			*ppDestGrp=&rFromGrp;
			SCopy(Filename, szDstName, _MAX_PATH);
			}
		}
	return iLocalLoaders;
	}
int32_t C4Network2ResChunkData::GetChunkToRetrieve(const C4Network2ResChunkData &Available, int32_t iLoadingCnt, int32_t *pLoading) const
{
	// (this version is highly calculation-intensitive, yet the most satisfactory
	//  solution I could find)

	// find everything that should not be retrieved
	C4Network2ResChunkData ChData; Available.GetNegative(ChData);
	ChData.Merge(*this);
	for (int32_t i = 0; i < iLoadingCnt; i++)
		ChData.AddChunk(pLoading[i]);
	// nothing to retrieve?
	if (ChData.isComplete()) return -1;
	// invert to get everything that should be retrieved
	C4Network2ResChunkData ChData2; ChData.GetNegative(ChData2);
	// select chunk (random)
	int32_t iRetrieveChunk = SafeRandom(ChData2.getPresentChunkCnt());
	// return
	return ChData2.getPresentChunk(iRetrieveChunk);
}
bool C4MusicSystem::Play(const char *szSongname, bool fLoop, int fadetime_ms, double max_resume_time, bool allow_break)
{
	// pause is done
	is_waiting = false;
	upcoming_music_file = NULL;

	// music off?
	if (Game.IsRunning ? !Config.Sound.RXMusic : !Config.Sound.FEMusic)
		return false;

	// info
	if (::Config.Sound.Verbose)
	{
		LogF("MusicSystem: Play(\"%s\", %s, %d, %.3lf, %s)", szSongname ? szSongname : "(null)", fLoop ? "true" : "false", fadetime_ms, max_resume_time, allow_break ? "true" : "false");
	}

	C4MusicFile* NewFile = NULL;

	// Specified song name
	if (szSongname && szSongname[0])
	{
		// Search in list
		for (NewFile = Songs; NewFile; NewFile = NewFile->pNext)
		{
			char songname[_MAX_FNAME + 1];
			SCopy(szSongname, songname); DefaultExtension(songname, "mid");
			if (SEqual(GetFilename(NewFile->FileName), songname))
				break;
			SCopy(szSongname, songname); DefaultExtension(songname, "ogg");
			if (SEqual(GetFilename(NewFile->FileName), songname))
				break;
		}
	}
	else
	{
		// When resuming, prefer songs that were interrupted before
		if (max_resume_time > 0)
		{
			C4TimeMilliseconds t_now = C4TimeMilliseconds::Now();
			for (C4MusicFile *check_file = Songs; check_file; check_file = check_file->pNext)
				if (!check_file->NoPlay)
				{
					if (check_file->HasResumePos() && check_file->GetRemainingTime() > max_resume_time)
						if (!music_max_position_memory || (t_now - check_file->GetLastInterruptionTime() <= music_max_position_memory*1000))
							if (!NewFile || NewFile->LastPlayed < check_file->LastPlayed)
								NewFile = check_file;
				}
		}

		// Random song
		if (!NewFile)
		{
			// Intead of a new song, is a break also allowed?
			if (allow_break) ScheduleWaitTime();
			if (!is_waiting)
			{
				if (::Config.Sound.Verbose) LogF("  ASongCount=%d SCounter=%d", ASongCount, SCounter);
				// try to find random song
				int32_t new_file_playability = 0, new_file_num_rolls = 0;
				for (C4MusicFile *check_file = Songs; check_file; check_file = check_file->pNext)
				{
					if (!check_file->NoPlay)
					{
						// Categorize song playability:
						// 0 = no song found yet
						// 1 = song was played recently
						// 2 = song not played recently
						// 3 = song was not played yet
						int32_t check_file_playability = (check_file->LastPlayed < 0) ? 3 : (SCounter - check_file->LastPlayed <= ASongCount / 2) ? 1 : 2;
						if (::Config.Sound.Verbose) LogF("  Song LastPlayed %d [%d] (%s)", int(check_file->LastPlayed), int(check_file_playability), check_file->GetDebugInfo().getData());
						if (check_file_playability > new_file_playability)
						{
							// Found much better fit. Play this and reset number of songs found in same plyability
							new_file_num_rolls = 1;
							NewFile = check_file;
							new_file_playability = check_file_playability;
						}
						else if (check_file_playability == new_file_playability)
						{
							// Found a fit in the same playability category: Roll for it
							if (!SafeRandom(++new_file_num_rolls)) NewFile = check_file;
						}
						else
						{
							// Worse playability - ignore this song
						}
					}
				}
			}

		}
	}

	// File (or wait time) found?
	if (!NewFile && !is_waiting)
		return false;

	// Stop/Fade out old music
	bool is_fading = (fadetime_ms && NewFile != PlayMusicFile && PlayMusicFile);
	if (!is_fading)
	{
		Stop();
	}
	else
	{
		C4TimeMilliseconds tNow = C4TimeMilliseconds::Now();
		if (FadeMusicFile)
		{
			if (FadeMusicFile == NewFile && FadeMusicFile->IsLooping() == fLoop && tNow < FadeTimeEnd)
			{
				// Fading back to a song while it wasn't fully faded out yet. Just swap our pointers and fix timings for that.
				FadeMusicFile = PlayMusicFile;
				PlayMusicFile = NewFile;
				FadeTimeEnd = tNow + fadetime_ms * (tNow - FadeTimeStart) / (FadeTimeEnd - FadeTimeStart);
				FadeTimeStart = FadeTimeEnd - fadetime_ms;
				return true;
			}
			else
			{
				// Fading to a third song while the previous wasn't faded out yet
				// That's pretty chaotic anyway, so just cancel the last song
				// Also happens if fading should already be done, in which case it won't harm to stop now
				// (It would stop on next call to Execute() anyway)
				// Also happens when fading back to the same song but loop status changes, but that should be really uncommon.
				FadeMusicFile->Stop();
			}

		}
		FadeMusicFile = PlayMusicFile;
		PlayMusicFile = NULL;
		FadeTimeStart = tNow;
		FadeTimeEnd = FadeTimeStart + fadetime_ms;
	}

	// Waiting?
	if (!NewFile) return false;

	// If the old file is being faded out and a new file would just start, start delayed and without fading
	// so the beginning of a song isn't faded unnecesserily (because our songs often start very abruptly)
	if (is_fading && (!NewFile->HasResumePos() || NewFile->GetRemainingTime() <= max_resume_time))
	{
		upcoming_music_file = NewFile;
		is_waiting = true;
		wait_time_end = FadeTimeEnd;
		return false;
	}

	if (!Play(NewFile, fLoop, max_resume_time)) return false;

	if (is_fading) PlayMusicFile->SetVolume(0);

	return true;
}
Exemple #10
0
int32_t FnFxFireTimer(C4AulContext *ctx, C4Object *pObj, int32_t iNumber,
                      int32_t iTime) {
  // safety
  if (!pObj)
    return C4Fx_Execute_Kill;

  // get cause
  int32_t iCausedByPlr = NO_OWNER;
  C4Effect *pEffect;
  if (pEffect = pObj->pEffects)
    if (pEffect = pEffect->Get(iNumber, true)) {
      iCausedByPlr = FxFireVarCausedBy(pEffect).getInt();
      if (!ValidPlr(iCausedByPlr))
        iCausedByPlr = NO_OWNER;
    }

  // causes on object
  pObj->ExecFire(iNumber, iCausedByPlr);

  // special effects only if loaded
  if (!Game.Particles.IsFireParticleLoaded())
    return C4Fx_OK;

  // get effect: May be NULL after object fire execution, in which case the fire
  // has been extinguished
  if (!pObj->GetOnFire())
    return C4Fx_Execute_Kill;
  if (!(pEffect = pObj->pEffects))
    return C4Fx_Execute_Kill;
  if (!(pEffect = pEffect->Get(iNumber, true)))
    return C4Fx_Execute_Kill;

  /* Fire execution behaviour transferred from script (FIRE) */

  // get fire mode
  int32_t iFireMode = FxFireVarMode(pEffect).getInt();

  // special effects only each four frames, except for objects (e.g.:
  // Projectiles)
  if (iTime % 4 && iFireMode != C4Fx_FireMode_Object)
    return C4Fx_OK;

  // no gfx for contained
  if (pObj->Contained)
    return C4Fx_OK;

  // some constant effect parameters for this object
  int32_t iWidth = Max<int32_t>(pObj->Def->Shape.Wdt, 1),
          iHeight = pObj->Def->Shape.Hgt,
          iYOff = iHeight / 2 - pObj->Def->Shape.FireTop;

  int32_t iCount = int32_t(sqrt(double(iWidth * iHeight)) /
                           4); // Number of particles per execution
  const int32_t iBaseParticleSize =
      30; // With of particles in pixels/10, w/o add of values below
  const int32_t iParticleSizeDiff = 10; // Size variation among particles
  const int32_t iRelParticleSize =
      12; // Influence of object size on particle size

  // some varying effect parameters
  int32_t iX = pObj->x, iY = pObj->y;
  int32_t iXDir, iYDir, iCon, iWdtCon, iA, iSize;

  // get remainign size (%)
  iCon = iWdtCon = Max<int32_t>((100 * pObj->GetCon()) / FullCon, 1);
  if (!pObj->Def->GrowthType)
    // fixed width for not-stretched-objects
    if (iWdtCon < 100)
      iWdtCon = 100;

  // regard non-center object offsets
  iX += pObj->Shape.x + pObj->Shape.Wdt / 2;
  iY += pObj->Shape.y + pObj->Shape.Hgt / 2;

  // apply rotation
  float fRot[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
  if (pObj->r && pObj->Def->Rotateable) {
    fRot[0] = (float)cosf((float)(pObj->r * pi / 180.0));
    fRot[1] = (float)-sinf((float)(pObj->r * pi / 180.0));
    fRot[2] = -fRot[1];
    fRot[3] = fRot[0];
    // rotated objects usually better burn from the center
    if (iYOff > 0)
      iYOff = 0;
  }

  // Adjust particle number by con
  iCount = Max(2, iCount * iWdtCon / 100);

  // calc base for particle size parameter
  iA = (int32_t)(sqrt(sqrt(double(iWidth * iHeight)) * (iCon + 20) / 120) *
                 iRelParticleSize);

  // create a double set of particles; first quarter normal (Fire); remaining
  // three quarters additive (Fire2)
  for (int32_t i = 0; i < iCount * 2; ++i) {
    // calc actual size to be used in this frame
    // Using Random instead of SafeRandom would be safe here
    // However, since it's just affecting particles there's no need to use
    // synchronized random values
    iSize = SafeRandom(iParticleSizeDiff + 1) + iBaseParticleSize -
            iParticleSizeDiff / 2 - 1 + iA;

    // get particle target list
    C4ParticleList *pParticleList =
        SafeRandom(4) ? &(pObj->BackParticles) : &(pObj->FrontParticles);

    // get particle def and color
    C4ParticleDef *pPartDef;
    DWORD dwClr;
    if (i < iCount / 2) {
      dwClr = 0x32004000 + ((SafeRandom(59) + 196) << 16);
      pPartDef = Game.Particles.pFire1;
    } else {
      dwClr = 0xffffff;
      pPartDef = Game.Particles.pFire2;
    }
    if (iFireMode == C4Fx_FireMode_Object)
      dwClr += 0x62000000;

    // get particle creation pos...
    int32_t iRandX = SafeRandom(iWidth + 1) - iWidth / 2 - 1;

    int32_t iPx = iRandX * iWdtCon / 100;
    int32_t iPy = iYOff * iCon / 100;
    if (iFireMode == C4Fx_FireMode_LivingVeg)
      iPy -= iPx * iPx * 100 / iWidth /
             iWdtCon; // parable form particle pos on livings

    // ...and movement speed
    if (iFireMode != C4Fx_FireMode_Object) {
      // ...for normal fire proc
      iXDir = iRandX * iCon / 400 - int32_t(iPx / 3) -
              int32_t(fixtof(pObj->xdir) * 3);
      iYDir = -SafeRandom(15 + iHeight * iCon / 300) - 1 -
              int32_t(fixtof(pObj->ydir) * 3);
    } else {
      // ...for objects
      iXDir = -int32_t(fixtof(pObj->xdir) * 3);
      iYDir = -int32_t(fixtof(pObj->ydir) * 3);
      if (!iYDir)
        iYDir = -SafeRandom(13 + iHeight / 4) - 1;
    }

    // OK; create it!
    Game.Particles.Create(pPartDef, float(iX) + fRot[0] * iPx + fRot[1] * iPy,
                          float(iY) + fRot[2] * iPx + fRot[3] * iPy,
                          (float)iXDir / 10.0f, (float)iYDir / 10.0f,
                          (float)iSize / 10.0f, dwClr, pParticleList, pObj);
  }

  return C4Fx_OK;
}