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); }
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; }
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; }