bool loadPlayerScript(QString path, int player, int difficulty) { ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "Player index %d out of bounds", player); QScriptEngine *engine = new QScriptEngine(); UDWORD size; char *bytes = NULL; if (!loadFile(path.toAscii().constData(), &bytes, &size)) { debug(LOG_ERROR, "Failed to read script file \"%s\"", path.toAscii().constData()); return false; } QString source = QString::fromAscii(bytes, size); free(bytes); QScriptSyntaxCheckResult syntax = QScriptEngine::checkSyntax(source); ASSERT_OR_RETURN(false, syntax.state() == QScriptSyntaxCheckResult::Valid, "Syntax error in %s line %d: %s", path.toAscii().constData(), syntax.errorLineNumber(), syntax.errorMessage().toAscii().constData()); // Special functions engine->globalObject().setProperty("setTimer", engine->newFunction(js_setTimer)); engine->globalObject().setProperty("queue", engine->newFunction(js_queue)); engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer)); engine->globalObject().setProperty("include", engine->newFunction(js_include)); engine->globalObject().setProperty("bind", engine->newFunction(js_bind)); // Special global variables //== \item[version] Current version of the game, set in \emph{major.minor} format. engine->globalObject().setProperty("version", "3.2", QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[selectedPlayer] The player ontrolled by the client on which the script runs. engine->globalObject().setProperty("selectedPlayer", selectedPlayer, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[gameTime] The current game time. Updated before every invokation of a script. engine->globalObject().setProperty("gameTime", gameTime, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[difficulty] The currently set campaign difficulty, or the current AI's difficulty setting. It will be one of //== EASY, MEDIUM, HARD or INSANE. engine->globalObject().setProperty("difficulty", difficulty, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[mapName] The name of the current map. engine->globalObject().setProperty("mapName", game.map, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[baseType] The type of base that the game starts with. It will be one of CAMP_CLEAN, CAMP_BASE or CAMP_WALLS. engine->globalObject().setProperty("baseType", game.base, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[alliancesType] The type of alliances permitted in this game. It will be one of NO_ALLIANCES, ALLIANCES or ALLIANCES_TEAMS. engine->globalObject().setProperty("alliancesType", game.alliance, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[powerType] The power level set for this game. engine->globalObject().setProperty("powerType", game.power, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[maxPlayers] The number of active players in this game. engine->globalObject().setProperty("maxPlayers", game.maxPlayers, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[scavengers] Whether or not scavengers are activated in this game. engine->globalObject().setProperty("scavengers", game.scavengers, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[mapWidth] Width of map in tiles. engine->globalObject().setProperty("mapWidth", mapWidth, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[mapHeight] Height of map in tiles. engine->globalObject().setProperty("mapHeight", mapHeight, QScriptValue::ReadOnly | QScriptValue::Undeletable); //== \item[scavengerPlayer] Index of scavenger player. (3.2+ only) engine->globalObject().setProperty("scavengerPlayer", scavengerPlayer(), QScriptValue::ReadOnly | QScriptValue::Undeletable); // Regular functions registerFunctions(engine); // Remember internal, reserved names QScriptValueIterator it(engine->globalObject()); while (it.hasNext()) { it.next(); internalNamespace.insert(it.name(), 1); } // We need to always save the 'me' special variable. //== \item[me] The player the script is currently running as. engine->globalObject().setProperty("me", player, QScriptValue::ReadOnly | QScriptValue::Undeletable); QScriptValue result = engine->evaluate(source, path); ASSERT_OR_RETURN(false, !engine->hasUncaughtException(), "Uncaught exception at line %d, file %s: %s", engine->uncaughtExceptionLineNumber(), path.toAscii().constData(), result.toString().toAscii().constData()); // Register script scripts.push_back(engine); debug(LOG_SAVE, "Created script engine %d for player %d from %s", scripts.size() - 1, player, path.toUtf8().constData()); return true; }
/* Create a feature on the map */ FEATURE * buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y,bool FromSave) { //try and create the Feature FEATURE *psFeature = new FEATURE(generateSynchronisedObjectId(), psStats); if (psFeature == NULL) { debug(LOG_WARNING, "Feature couldn't be built."); return NULL; } // features are not in the cluster system // this will cause an assert when they still end up there psFeature->cluster = ~0; //add the feature to the list - this enables it to be drawn whilst being built addFeature(psFeature); // snap the coords to a tile if (!FromSave) { x = (x & ~TILE_MASK) + psStats->baseWidth %2 * TILE_UNITS/2; y = (y & ~TILE_MASK) + psStats->baseBreadth%2 * TILE_UNITS/2; } else { if ((x & TILE_MASK) != psStats->baseWidth %2 * TILE_UNITS/2 || (y & TILE_MASK) != psStats->baseBreadth%2 * TILE_UNITS/2) { debug(LOG_WARNING, "Feature not aligned. position (%d,%d), size (%d,%d)", x, y, psStats->baseWidth, psStats->baseBreadth); } } psFeature->pos.x = x; psFeature->pos.y = y; StructureBounds b = getStructureBounds(psFeature); // get the terrain average height int foundationMin = INT32_MAX; int foundationMax = INT32_MIN; for (int breadth = 0; breadth <= b.size.y; ++breadth) { for (int width = 0; width <= b.size.x; ++width) { int h = map_TileHeight(b.map.x + width, b.map.y + breadth); foundationMin = std::min(foundationMin, h); foundationMax = std::max(foundationMax, h); } } //return the average of max/min height int height = (foundationMin + foundationMax) / 2; if (psStats->subType == FEAT_TREE) { psFeature->rot.direction = gameRand(DEG_360); } else { psFeature->rot.direction = 0; } psFeature->body = psStats->body; psFeature->periodicalDamageStart = 0; psFeature->periodicalDamage = 0; // it has never been drawn psFeature->sDisplay.frameNumber = 0; memset(psFeature->seenThisTick, 0, sizeof(psFeature->seenThisTick)); memset(psFeature->visible, 0, sizeof(psFeature->visible)); // set up the imd for the feature psFeature->sDisplay.imd = psStats->psImd; ASSERT_OR_RETURN(NULL, psFeature->sDisplay.imd, "No IMD for feature"); // make sure we have an imd. for (int breadth = 0; breadth < b.size.y; ++breadth) { for (int width = 0; width < b.size.x; ++width) { MAPTILE *psTile = mapTile(b.map.x + width, b.map.y + breadth); //check not outside of map - for load save game ASSERT_OR_RETURN(NULL, b.map.x + width < mapWidth, "x coord bigger than map width - %s, id = %d", getName(psFeature->psStats), psFeature->id); ASSERT_OR_RETURN(NULL, b.map.y + breadth < mapHeight, "y coord bigger than map height - %s, id = %d", getName(psFeature->psStats), psFeature->id); if (width != psStats->baseWidth && breadth != psStats->baseBreadth) { if (TileHasFeature(psTile)) { FEATURE *psBlock = (FEATURE *)psTile->psObject; debug(LOG_ERROR, "%s(%d) already placed at (%d+%d, %d+%d) when trying to place %s(%d) at (%d+%d, %d+%d) - removing it", getName(psBlock->psStats), psBlock->id, map_coord(psBlock->pos.x), psBlock->psStats->baseWidth, map_coord(psBlock->pos.y), psBlock->psStats->baseBreadth, getName(psFeature->psStats), psFeature->id, b.map.x, b.size.x, b.map.y, b.size.y); removeFeature(psBlock); } psTile->psObject = (BASE_OBJECT*)psFeature; // if it's a tall feature then flag it in the map. if (psFeature->sDisplay.imd->max.y > TALLOBJECT_YMAX) { auxSetBlocking(b.map.x + width, b.map.y + breadth, AIR_BLOCKED); } if (psStats->subType != FEAT_GEN_ARTE && psStats->subType != FEAT_OIL_DRUM) { auxSetBlocking(b.map.x + width, b.map.y + breadth, FEATURE_BLOCKED); } } if( (!psStats->tileDraw) && (FromSave == false) ) { psTile->height = height; } } } psFeature->pos.z = map_TileHeight(b.map.x, b.map.y);//jps 18july97 return psFeature; }
bool assertValidImage(IMAGEFILE *imageFile, unsigned id) { ASSERT_OR_RETURN(false, id < imageFile->imageDefs.size(), "Out of range 1: %u/%d", id, (int)imageFile->imageDefs.size()); ASSERT_OR_RETURN(false, imageFile->imageDefs[id].TPageID < imageFile->pages.size(), "Out of range 2: %u", imageFile->imageDefs[id].TPageID); return true; }
/* This returns true if the key is currently depressed */ bool keyDown(KEY_CODE code) { ASSERT_OR_RETURN(false, code < KEY_MAXSCAN, "Invalid keycode of %d!", (int)code); return (aKeyState[code].state != KEY_UP); }
/*! * Load shape levels recursively * \param ppFileData Pointer to the data (usualy read from a file) * \param FileDataEnd ??? * \param nlevels Number of levels to load * \return pointer to iFSDShape structure (or NULL on error) * \pre ppFileData loaded * \post s allocated */ static iIMDShape *_imd_load_level(const char **ppFileData, const char *FileDataEnd, int nlevels, int pieVersion) { const char *pFileName = GetLastResourceFilename(); // Last loaded filename const char *pFileData = *ppFileData; char buffer[PATH_MAX] = {'\0'}; int cnt = 0, n = 0, i; iIMDShape *s = NULL; float dummy; if (nlevels == 0) { return NULL; } i = sscanf(pFileData, "%255s %n", buffer, &cnt); ASSERT_OR_RETURN(NULL, i == 1, "Bad directive following LEVEL"); s = new iIMDShape; // Optionally load and ignore deprecated MATERIALS directive if (strcmp(buffer, "MATERIALS") == 0) { i = sscanf(pFileData, "%255s %f %f %f %f %f %f %f %f %f %f%n", buffer, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &cnt); ASSERT_OR_RETURN(NULL, i == 11, "Bad MATERIALS directive"); debug(LOG_WARNING, "MATERIALS directive no longer supported!"); pFileData += cnt; } else if (strcmp(buffer, "SHADERS") == 0) { char vertex[PATH_MAX], fragment[PATH_MAX]; if (sscanf(pFileData, "%255s %255s %255s%n", buffer, vertex, fragment, &cnt) != 3) { debug(LOG_ERROR, "%s shader corrupt: %s", pFileName, buffer); return NULL; } s->shaderProgram = pie_LoadShader(pFileName, vertex, fragment); pFileData += cnt; } if (sscanf(pFileData, "%255s %d%n", buffer, &s->npoints, &cnt) != 2) { debug(LOG_ERROR, "_imd_load_level(2): file corrupt"); return NULL; } pFileData += cnt; // load points ASSERT_OR_RETURN(NULL, strcmp(buffer, "POINTS") == 0, "Expecting 'POINTS' directive, got: %s", buffer); _imd_load_points(&pFileData, s); if (sscanf(pFileData, "%255s %d%n", buffer, &s->npolys, &cnt) != 2) { debug(LOG_ERROR, "_imd_load_level(3): file corrupt"); return NULL; } pFileData += cnt; ASSERT_OR_RETURN(NULL, strcmp(buffer, "POLYGONS") == 0, "Expecting 'POLYGONS' directive, got: %s", buffer); _imd_load_polys(&pFileData, s, pieVersion); // NOW load optional stuff while (!AtEndOfFile(pFileData, FileDataEnd)) // check for end of file (give or take white space) { // Scans in the line ... if we don't get 2 parameters then quit if (sscanf(pFileData, "%255s %d%n", buffer, &n, &cnt) != 2) { break; } pFileData += cnt; if (strcmp(buffer, "LEVEL") == 0) // check for next level { debug(LOG_3D, "imd[_load_level] = npoints %d, npolys %d", s->npoints, s->npolys); s->next = _imd_load_level(&pFileData, FileDataEnd, nlevels - 1, pieVersion); } else if (strcmp(buffer, "CONNECTORS") == 0) { //load connector stuff s->nconnectors = n; _imd_load_connectors(&pFileData, s); } else { debug(LOG_ERROR, "(_load_level) unexpected directive %s %d", buffer, n); break; } } // FINALLY, massage the data into what can stream directly to OpenGL glGenBuffers(VBO_COUNT, s->buffers); vertexCount = 0; for (int k = 0; k < MAX(1, s->numFrames); k++) { // Go through all polygons for each frame for (int i = 0; i < s->npolys; i++) { const iIMDPoly *pPolys = &s->polys[i]; // Do we already have the vertex data for this polygon? indices.append(addVertex(s, 0, pPolys, k)); indices.append(addVertex(s, 1, pPolys, k)); indices.append(addVertex(s, 2, pPolys, k)); } } glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_VERTEX]); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_NORMAL]); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(GLfloat), normals.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->buffers[VBO_INDEX]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(uint16_t), indices.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_TEXCOORD]); glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(GLfloat), texcoords.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind indices.resize(0); vertices.resize(0); texcoords.resize(0); normals.resize(0); *ppFileData = pFileData; return s; }
// Run from screen.c on init. Do not change the order of loading here! First ones are enumerated. bool pie_LoadShaders() { pie_internal::SHADER_PROGRAM program; int result; // Load some basic shaders memset(&program, 0, sizeof(program)); pie_internal::shaderProgram.push_back(program); int shaderEnum = 0; // TCMask shader for map-placed models with advanced lighting debug(LOG_3D, "Loading shader: SHADER_COMPONENT"); result = pie_LoadShader("Component program", "shaders/tcmask.vert", "shaders/tcmask.frag", { "colour", "teamcolour", "stretch", "tcmask", "fogEnabled", "normalmap", "specularmap", "ecmEffect", "alphaTest", "graphicsCycle", "ModelViewMatrix", "ModelViewProjectionMatrix", "NormalMatrix", "lightPosition", "sceneColor", "ambient", "diffuse", "specular", "fogEnd", "fogStart", "fogColor" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_COMPONENT, "Failed to load component shader"); // TCMask shader for buttons with flat lighting debug(LOG_3D, "Loading shader: SHADER_BUTTON"); result = pie_LoadShader("Button program", "shaders/button.vert", "shaders/button.frag", { "colour", "teamcolour", "stretch", "tcmask", "fogEnabled", "normalmap", "specularmap", "ecmEffect", "alphaTest", "graphicsCycle", "ModelViewMatrix", "ModelViewProjectionMatrix", "NormalMatrix", "lightPosition", "sceneColor", "ambient", "diffuse", "specular", "fogEnd", "fogStart", "fogColor" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_BUTTON, "Failed to load button shader"); // Plain shader for no lighting debug(LOG_3D, "Loading shader: SHADER_NOLIGHT"); result = pie_LoadShader("Plain program", "shaders/nolight.vert", "shaders/nolight.frag", { "colour", "teamcolour", "stretch", "tcmask", "fogEnabled", "normalmap", "specularmap", "ecmEffect", "alphaTest", "graphicsCycle", "ModelViewMatrix", "ModelViewProjectionMatrix", "NormalMatrix", "lightPosition", "sceneColor", "ambient", "diffuse", "specular", "fogEnd", "fogStart", "fogColor" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_NOLIGHT, "Failed to load no-lighting shader"); debug(LOG_3D, "Loading shader: SHADER_TERRAIN"); result = pie_LoadShader("terrain program", "shaders/terrain_water.vert", "shaders/terrain.frag", { "ModelViewProjectionMatrix", "paramx1", "paramy1", "paramx2", "paramy2", "tex", "lightmap_tex", "textureMatrix1", "textureMatrix2", "fogEnabled", "fogEnd", "fogStart", "fogColor" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_TERRAIN, "Failed to load terrain shader"); debug(LOG_3D, "Loading shader: SHADER_TERRAIN_DEPTH"); result = pie_LoadShader("terrain_depth program", "shaders/terrain_water.vert", "shaders/terraindepth.frag", { "ModelViewProjectionMatrix", "paramx2", "paramy2", "lightmap_tex", "textureMatrix1", "textureMatrix2" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_TERRAIN_DEPTH, "Failed to load terrain_depth shader"); debug(LOG_3D, "Loading shader: SHADER_DECALS"); result = pie_LoadShader("decals program", "shaders/decals.vert", "shaders/decals.frag", { "ModelViewProjectionMatrix", "paramxlight", "paramylight", "tex", "lightmap_tex", "lightTextureMatrix", "fogEnabled", "fogEnd", "fogStart", "fogColor" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_DECALS, "Failed to load decals shader"); debug(LOG_3D, "Loading shader: SHADER_WATER"); result = pie_LoadShader("water program", "shaders/terrain_water.vert", "shaders/water.frag", { "ModelViewProjectionMatrix", "paramx1", "paramy1", "paramx2", "paramy2", "tex1", "tex2", "textureMatrix1", "textureMatrix2", "fogEnabled", "fogEnd", "fogStart" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_WATER, "Failed to load water shader"); // Rectangular shader debug(LOG_3D, "Loading shader: SHADER_RECT"); result = pie_LoadShader("Rect program", "shaders/rect.vert", "shaders/rect.frag", { "transformationMatrix", "color" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_RECT, "Failed to load rect shader"); // Textured rectangular shader debug(LOG_3D, "Loading shader: SHADER_TEXRECT"); result = pie_LoadShader("Textured rect program", "shaders/rect.vert", "shaders/texturedrect.frag", { "transformationMatrix", "tuv_offset", "tuv_scale", "color", "texture" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_TEXRECT, "Failed to load textured rect shader"); debug(LOG_3D, "Loading shader: SHADER_GFX_COLOUR"); result = pie_LoadShader("gfx_color program", "shaders/gfx.vert", "shaders/gfx.frag", { "posMatrix" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_GFX_COLOUR, "Failed to load textured gfx shader"); debug(LOG_3D, "Loading shader: SHADER_GFX_TEXT"); result = pie_LoadShader("gfx_text program", "shaders/gfx.vert", "shaders/texturedrect.frag", { "posMatrix", "color", "texture" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_GFX_TEXT, "Failed to load textured gfx shader"); debug(LOG_3D, "Loading shader: SHADER_GENERIC_COLOR"); result = pie_LoadShader("generic color program", "shaders/generic.vert", "shaders/rect.frag", { "ModelViewProjectionMatrix", "color" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_GENERIC_COLOR, "Failed to load generic color shader"); debug(LOG_3D, "Loading shader: SHADER_LINE"); result = pie_LoadShader("line program", "shaders/line.vert", "shaders/rect.frag", { "from", "to", "color", "ModelViewProjectionMatrix" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_LINE, "Failed to load line shader"); // Text shader debug(LOG_3D, "Loading shader: SHADER_TEXT"); result = pie_LoadShader("Text program", "shaders/rect.vert", "shaders/text.frag", { "transformationMatrix", "tuv_offset", "tuv_scale", "color", "texture" }); ASSERT_OR_RETURN(false, result && ++shaderEnum == SHADER_TEXT, "Failed to load text shader"); pie_internal::currentShaderMode = SHADER_NONE; GLbyte rect[] { 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1 }; glGenBuffers(1, &pie_internal::rectBuffer); glBindBuffer(GL_ARRAY_BUFFER, pie_internal::rectBuffer); glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLbyte), rect, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); return true; }
bool assertValidImage(IMAGEFILE *imageFile, unsigned id) { ASSERT_OR_RETURN(false, id < (unsigned)imageFile->NumImages, "Out of range 1: %u/%d", id, imageFile->NumImages); ASSERT_OR_RETURN(false, imageFile->ImageDefs[id].TPageID < MAX_NUM_TPAGEIDS, "Out of range 2: %u", imageFile->ImageDefs[id].TPageID); return true; }
ASR_RETVAL fpathAStarRoute(MOVE_CONTROL *psMove, PATHJOB *psJob) { ASR_RETVAL retval = ASR_OK; bool mustReverse = true; const PathCoord tileOrig(map_coord(psJob->origX), map_coord(psJob->origY)); const PathCoord tileDest(map_coord(psJob->destX), map_coord(psJob->destY)); PathCoord endCoord; // Either nearest coord (mustReverse = true) or orig (mustReverse = false). std::list<PathfindContext>::iterator contextIterator = fpathContexts.begin(); for (contextIterator = fpathContexts.begin(); contextIterator != fpathContexts.end(); ++contextIterator) { if (!contextIterator->matches(psJob->blockingMap, tileDest)) { // This context is not for the same droid type and same destination. continue; } // We have tried going to tileDest before. if (contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].iteration == contextIterator->iteration && contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].visited) { // Already know the path from orig to dest. endCoord = tileOrig; } else { // Need to find the path from orig to dest, continue previous exploration. fpathAStarReestimate(*contextIterator, tileOrig); endCoord = fpathAStarExplore(*contextIterator, tileOrig); } if (endCoord != tileOrig) { // orig turned out to be on a different island than what this context was used for, so can't use this context data after all. continue; } mustReverse = false; // We have the path from the nearest reachable tile to dest, to orig. break; // Found the path! Don't search more contexts. } if (contextIterator == fpathContexts.end()) { // We did not find an appropriate context. Make one. if (fpathContexts.size() < 30) { fpathContexts.push_back(PathfindContext()); } --contextIterator; // Init a new context, overwriting the oldest one if we are caching too many. // We will be searching from orig to dest, since we don't know where the nearest reachable tile to dest is. fpathInitContext(*contextIterator, psJob->blockingMap, tileOrig, tileOrig, tileDest); endCoord = fpathAStarExplore(*contextIterator, tileDest); contextIterator->nearestCoord = endCoord; } PathfindContext &context = *contextIterator; // return the nearest route if no actual route was found if (context.nearestCoord != tileDest) { retval = ASR_NEAREST; } // Get route, in reverse order. static std::vector<Vector2i> path; // Declared static to save allocations. path.clear(); PathCoord newP; for (PathCoord p = endCoord; p != context.tileS; p = newP) { ASSERT_OR_RETURN(ASR_FAILED, tileOnMap(p.x, p.y), "Assigned XY coordinates (%d, %d) not on map!", (int)p.x, (int)p.y); ASSERT_OR_RETURN(ASR_FAILED, path.size() < (unsigned)mapWidth*mapHeight, "Pathfinding got in a loop."); path.push_back(Vector2i(world_coord(p.x) + TILE_UNITS / 2, world_coord(p.y) + TILE_UNITS / 2)); PathExploredTile &tile = context.map[p.x + p.y*mapWidth]; newP = PathCoord(p.x - tile.dx, p.y - tile.dy); if (p == newP) { break; // We stopped moving, because we reached the closest reachable tile to context.tileS. Give up now. } } if (path.empty()) { // We are probably already in the destination tile. Go to the exact coordinates. path.push_back(Vector2i(psJob->destX, psJob->destY)); } else if (retval == ASR_OK) { // Found exact path, so use exact coordinates for last point, no reason to lose precision Vector2i v(psJob->destX, psJob->destY); if (mustReverse) { path.front() = v; } else { path.back() = v; } } // TODO FIXME once we can change numPoints to something larger than uint16_t psMove->numPoints = std::min<int>(UINT16_MAX, path.size()); // Allocate memory psMove->asPath = static_cast<Vector2i *>(malloc(sizeof(*psMove->asPath) * path.size())); ASSERT(psMove->asPath, "Out of memory"); if (!psMove->asPath) { fpathHardTableReset(); return ASR_FAILED; } // get the route in the correct order // If as I suspect this is to reverse the list, then it's my suspicion that // we could route from destination to source as opposed to source to // destination. We could then save the reversal. to risky to try now...Alex M // // The idea is impractical, because you can't guarentee that the target is // reachable. As I see it, this is the reason why psNearest got introduced. // -- Dennis L. // // If many droids are heading towards the same destination, then destination // to source would be faster if reusing the information in nodeArray. --Cyp if (mustReverse) { // Copy the list, in reverse. std::copy(path.rbegin(), path.rend(), psMove->asPath); if (!context.isBlocked(tileOrig.x, tileOrig.y)) // If blocked, searching from tileDest to tileOrig wouldn't find the tileOrig tile. { // Next time, search starting from nearest reachable tile to the destination. fpathInitContext(context, psJob->blockingMap, tileDest, context.nearestCoord, tileOrig); } } else { // Copy the list. std::copy(path.begin(), path.end(), psMove->asPath); } // Move context to beginning of last recently used list. if (contextIterator != fpathContexts.begin()) // Not sure whether or not the splice is a safe noop, if equal. { fpathContexts.splice(fpathContexts.begin(), fpathContexts, contextIterator); } psMove->destination = psMove->asPath[path.size() - 1]; return retval; }
// not a direct script function but a helper for scrSkDefenseLocation and scrSkDefenseLocationB static bool defenseLocation(bool variantB) { SDWORD *pX,*pY,statIndex,statIndex2; UDWORD x,y,gX,gY,dist,player,nearestSoFar,count; GATEWAY *psGate,*psChosenGate; DROID *psDroid; UDWORD x1,x2,x3,x4,y1,y2,y3,y4; bool noWater; UDWORD minCount; UDWORD offset; if (!stackPopParams(6, VAL_REF|VAL_INT, &pX, VAL_REF|VAL_INT, &pY, ST_STRUCTURESTAT, &statIndex, ST_STRUCTURESTAT, &statIndex2, ST_DROID, &psDroid, VAL_INT, &player) ) { debug(LOG_ERROR,"defenseLocation: failed to pop"); return false; } if (player >= MAX_PLAYERS) { ASSERT( false, "defenseLocation:player number is too high" ); return false; } ASSERT_OR_RETURN( false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats); ASSERT_OR_RETURN( false, statIndex2 < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex2, numStructureStats); STRUCTURE_STATS *psWStats = (asStructureStats + statIndex2); // check for wacky coords. if( *pX < 0 || *pX > world_coord(mapWidth) || *pY < 0 || *pY > world_coord(mapHeight) ) { goto failed; } x = map_coord(*pX); // change to tile coords. y = map_coord(*pY); // go down the gateways, find the nearest gateway with >1 empty tiles nearestSoFar = UDWORD_MAX; psChosenGate = NULL; for (psGate = gwGetGateways(); psGate; psGate = psGate->psNext) { if (auxTile(psGate->x1, psGate->y1, player) & AUXBITS_THREAT) { continue; // enemy can shoot there, not safe to build } count = 0; noWater = true; // does it have >1 tile unoccupied. if(psGate->x1 == psGate->x2) {// vert //skip gates that are too short if(variantB && (psGate->y2 - psGate->y1) <= 2) { continue; } gX = psGate->x1; for(gY=psGate->y1;gY <= psGate->y2; gY++) { if(! TileIsOccupied(mapTile(gX,gY) )) { count++; } if (terrainType(mapTile(gX,gY)) == TER_WATER) { noWater = false; } } } else {// horiz //skip gates that are too short if(variantB && (psGate->x2 - psGate->x1) <= 2) { continue; } gY = psGate->y1; for(gX=psGate->x1;gX <= psGate->x2; gX++) { if(! TileIsOccupied(mapTile(gX,gY) )) { count++; } if (terrainType(mapTile(gX,gY)) == TER_WATER) { noWater = false; } } } if(variantB) { minCount = 2; } else { minCount = 1; } if(count > minCount && noWater) //<NEW> min 2 tiles { // ok it's free. Is it the nearest one yet? /* Get gateway midpoint */ gX = (psGate->x1 + psGate->x2)/2; gY = (psGate->y1 + psGate->y2)/2; /* Estimate the distance to it */ dist = iHypot(x - gX, y - gY); /* Is it best we've found? */ if(dist<nearestSoFar && dist<30) { /* Yes, then keep a record of it */ nearestSoFar = dist; psChosenGate = psGate; } } } if(!psChosenGate) // we have a gateway. { goto failed; } // find an unnocupied tile on that gateway. if(psChosenGate->x1 == psChosenGate->x2) {// vert gX = psChosenGate->x1; for(gY=psChosenGate->y1;gY <= psChosenGate->y2; gY++) { if(! TileIsOccupied(mapTile(gX,gY) )) { y = gY; x = gX; break; } } } else {// horiz gY = psChosenGate->y1; for(gX=psChosenGate->x1;gX <= psChosenGate->x2; gX++) { if(! TileIsOccupied(mapTile(gX,gY) )) { y = gY; x = gX; break; } } } // back to world coords and store result. *pX = world_coord(x) + (TILE_UNITS / 2); // return centre of tile. *pY = world_coord(y) + (TILE_UNITS / 2); scrFunctionResult.v.bval = true; if (!stackPushResult(VAL_BOOL,&scrFunctionResult)) // success { return false; } // order the droid to build two walls, one either side of the gateway. // or one in the case of a 2 size gateway. //find center of the gateway x = (psChosenGate->x1 + psChosenGate->x2)/2; y = (psChosenGate->y1 + psChosenGate->y2)/2; //find start pos of the gateway x1 = world_coord(psChosenGate->x1) + (TILE_UNITS / 2); y1 = world_coord(psChosenGate->y1) + (TILE_UNITS / 2); if(variantB) { offset = 2; } else { offset = 1; } if(psChosenGate->x1 == psChosenGate->x2) //vert { x2 = x1; //vert: end x pos of the first section = start x pos y2 = world_coord(y - 1) + TILE_UNITS / 2; //start y loc of the first sec x3 = x1; y3 = world_coord(y + offset) + TILE_UNITS / 2; } else //hor { x2 = world_coord(x - 1) + TILE_UNITS / 2; y2 = y1; x3 = world_coord(x+offset) + TILE_UNITS / 2; y3 = y1; } //end coords of the second section x4 = world_coord(psChosenGate->x2) + TILE_UNITS / 2; y4 = world_coord(psChosenGate->y2) + TILE_UNITS / 2; // first section. if(x1 == x2 && y1 == y2) //first sec is 1 tile only: ((2 tile gate) or (3 tile gate and first sec)) { orderDroidStatsLocDir(psDroid, DORDER_BUILD, psWStats, x1, y1, 0, ModeQueue); } else { orderDroidStatsTwoLocDir(psDroid, DORDER_LINEBUILD, psWStats, x1, y1, x2, y2, 0, ModeQueue); } // second section if(x3 == x4 && y3 == y4) { orderDroidStatsLocDirAdd(psDroid, DORDER_BUILD, psWStats, x3, y3, 0); } else { orderDroidStatsTwoLocDirAdd(psDroid, DORDER_LINEBUILD, psWStats, x3, y3, x4, y4, 0); } return true; failed: scrFunctionResult.v.bval = false; if (!stackPushResult(VAL_BOOL,&scrFunctionResult)) // failed! { return false; } return true; }
/* Find a widget in a screen from its ID number */ WIDGET *widgGetFromID(W_SCREEN *psScreen, UDWORD id) { ASSERT_OR_RETURN(NULL, psScreen != NULL, "Invalid screen pointer"); return widgFormGetFromID((W_FORM *)psScreen->psForm, id); }
// Find a route for an DROID to a location in world coordinates FPATH_RETVAL fpathDroidRoute(DROID* psDroid, SDWORD tX, SDWORD tY, FPATH_MOVETYPE moveType) { bool acceptNearest; PROPULSION_STATS *psPropStats = getPropulsionStats(psDroid); // override for AI to blast our way through stuff if (!isHumanPlayer(psDroid->player) && moveType == FMT_MOVE) { moveType = (psDroid->asWeaps[0].nStat == 0) ? FMT_MOVE : FMT_ATTACK; } ASSERT_OR_RETURN(FPR_FAILED, psPropStats != NULL, "invalid propulsion stats pointer"); ASSERT_OR_RETURN(FPR_FAILED, psDroid->type == OBJ_DROID, "We got passed an object that isn't a DROID!"); // check whether the end point of the route // is a blocking tile and find an alternative if it is if (psDroid->sMove.Status != MOVEWAITROUTE && fpathDroidBlockingTile(psDroid, map_coord(tX), map_coord(tY), moveType)) { // find the nearest non blocking tile to the DROID int minDist = SDWORD_MAX; int nearestDir = NUM_DIR; int dir; objTrace(psDroid->id, "BLOCKED(%d,%d) - trying workaround", map_coord(tX), map_coord(tY)); for (dir = 0; dir < NUM_DIR; dir++) { int x = map_coord(tX) + aDirOffset[dir].x; int y = map_coord(tY) + aDirOffset[dir].y; if (tileOnMap(x, y) && !fpathDroidBlockingTile(psDroid, x, y, moveType)) { // pick the adjacent tile closest to our starting point int tileDist = fpathDistToTile(x, y, psDroid->pos.x, psDroid->pos.y); if (tileDist < minDist) { minDist = tileDist; nearestDir = dir; } } if (dir == NUM_BASIC - 1 && nearestDir != NUM_DIR) { break; // found a solution without checking at greater distance } } if (nearestDir == NUM_DIR) { // surrounded by blocking tiles, give up objTrace(psDroid->id, "route to (%d, %d) failed - target blocked", map_coord(tX), map_coord(tY)); return FPR_FAILED; } else { tX = world_coord(map_coord(tX) + aDirOffset[nearestDir].x) + TILE_UNITS / 2; tY = world_coord(map_coord(tY) + aDirOffset[nearestDir].y) + TILE_UNITS / 2; objTrace(psDroid->id, "Workaround found at (%d, %d)", map_coord(tX), map_coord(tY)); } } switch (psDroid->order) { case DORDER_BUILD: case DORDER_HELPBUILD: // help to build a structure case DORDER_LINEBUILD: // 6 - build a number of structures in a row (walls + bridges) case DORDER_DEMOLISH: // demolish a structure case DORDER_REPAIR: acceptNearest = false; break; default: acceptNearest = true; break; } return fpathRoute(&psDroid->sMove, psDroid->id, psDroid->pos.x, psDroid->pos.y, tX, tY, psPropStats->propulsionType, psDroid->droidType, moveType, psDroid->player, acceptNearest); }
// //////////////////////////////////////////////////////////////////////////// static bool pushedKeyCombo(KEY_CODE subkey) { KEY_CODE metakey = KEY_IGNORE; KEY_MAPPING *pExist; KEY_MAPPING *psMapping; KEY_CODE alt; // check for // alt alt = (KEY_CODE)0; if (keyDown(KEY_RALT) || keyDown(KEY_LALT)) { metakey = KEY_LALT; alt = KEY_RALT; } // ctrl else if (keyDown(KEY_RCTRL) || keyDown(KEY_LCTRL)) { metakey = KEY_LCTRL; alt = KEY_RCTRL; } // shift else if (keyDown(KEY_RSHIFT) || keyDown(KEY_LSHIFT)) { metakey = KEY_LSHIFT; alt = KEY_RSHIFT; } // meta (cmd) else if (keyDown(KEY_RMETA) || keyDown(KEY_LMETA)) { metakey = KEY_LMETA; alt = KEY_RMETA; } // check if bound to a fixed combo. pExist = keyFindMapping(metakey, subkey); if (pExist && (pExist->status == KEYMAP_ALWAYS || pExist->status == KEYMAP_ALWAYS_PROCESS)) { selectedKeyMap = NULL; // unhighlght selected. return false; } /* Clear down mappings using these keys... But only if it isn't unassigned */ keyReAssignMapping(metakey, subkey, KEY_IGNORE, (KEY_CODE)KEY_MAXSCAN); /* Try and see if its there already - damn well should be! */ psMapping = keyGetMappingFromFunction((void *)selectedKeyMap->function); /* Cough if it's not there */ ASSERT_OR_RETURN(false, psMapping != NULL, "Trying to patch a non-existant function mapping - whoop whoop!!!"); /* Now alter it to the new values */ psMapping->metaKeyCode = metakey; psMapping->subKeyCode = subkey; // was "==" psMapping->status = KEYMAP_ASSIGNABLE; //must be if (alt) { psMapping->altMetaKeyCode = alt; } selectedKeyMap = NULL; // unhighlght selected . return true; }
// get the array data for an array operation static bool interpGetArrayVarData(INTERP_VAL **pip, VAL_CHUNK *psGlobals, SCRIPT_CODE *psProg, INTERP_VAL **ppsVal) { SDWORD i, dimensions, vals[VAR_MAX_DIMENSIONS]; UBYTE *elements; SDWORD size, val; INTERP_VAL *InstrPointer = *pip; UDWORD base, index; // get the base index of the array base = InstrPointer->v.ival & ARRAY_BASE_MASK; // get the number of dimensions dimensions = (InstrPointer->v.ival & ARRAY_DIMENSION_MASK) >> ARRAY_DIMENSION_SHIFT; ASSERT_OR_RETURN(false, base < psProg->numArrays, "Arrray base index out of range (%d should be less than %d)", base, psProg->numArrays); ASSERT_OR_RETURN(false, dimensions == psProg->psArrayInfo[base].dimensions, "Array dimensions do not match (%d vs %d)", dimensions, psProg->psArrayInfo[base].dimensions); // get the number of elements for each dimension elements = psProg->psArrayInfo[base].elements; // calculate the index of the array element size = 1; index = 0; for(i=dimensions-1; i>=0; i-=1) { if (!stackPopParams(1, VAL_INT, &val)) { return false; } if ( (val < 0) || (val >= elements[i]) ) { debug( LOG_ERROR, "interpGetArrayVarData: Array index for dimension %d out of range (passed index = %d, max index = %d)", i , val, elements[i]); return false; } index += val * size; size *= psProg->psArrayInfo[base].elements[i]; vals[i] = val; } // print out the debug trace if (interpTrace) { debug( LOG_NEVER, "%d->", base ); for(i=0; i<dimensions; i+= 1) { debug( LOG_NEVER, "[%d/%d]", vals[i], elements[i] ); } debug( LOG_NEVER, "(%d) ", index ); } // check the index is valid ASSERT_OR_RETURN(false, index <= psProg->arraySize, "Array indexes out of variable space (%d)", index); // get the variable data *ppsVal = interpGetVarData(psGlobals, psProg->psArrayInfo[base].base + index); // update the instruction pointer *pip += 1; return true; }
static Spacetime interpolateSpacetime(Spacetime st1, Spacetime st2, uint32_t t) { // Cyp says this should never happen, #3037 and #3238 say it does though. ASSERT_OR_RETURN(st1, st1.time != st2.time, "Spacetime overlap!"); return Spacetime(interpolatePos(st1.pos, st2.pos, st1.time, st2.time, t), interpolateRot(st1.rot, st2.rot, st1.time, st2.time, t), t); }
/* Remove a Feature and free it's memory */ bool destroyFeature(FEATURE *psDel, unsigned impactTime) { UDWORD widthScatter,breadthScatter,heightScatter, i; EFFECT_TYPE explosionSize; Vector3i pos; ASSERT_OR_RETURN(false, psDel != NULL, "Invalid feature pointer"); /* Only add if visible and damageable*/ if(psDel->visible[selectedPlayer] && psDel->psStats->damageable) { /* Set off a destruction effect */ /* First Explosions */ widthScatter = TILE_UNITS/2; breadthScatter = TILE_UNITS/2; heightScatter = TILE_UNITS/4; //set which explosion to use based on size of feature if (psDel->psStats->baseWidth < 2 && psDel->psStats->baseBreadth < 2) { explosionSize = EXPLOSION_TYPE_SMALL; } else if (psDel->psStats->baseWidth < 3 && psDel->psStats->baseBreadth < 3) { explosionSize = EXPLOSION_TYPE_MEDIUM; } else { explosionSize = EXPLOSION_TYPE_LARGE; } for(i=0; i<4; i++) { pos.x = psDel->pos.x + widthScatter - rand()%(2*widthScatter); pos.z = psDel->pos.y + breadthScatter - rand()%(2*breadthScatter); pos.y = psDel->pos.z + 32 + rand()%heightScatter; addEffect(&pos, EFFECT_EXPLOSION, explosionSize, false, NULL, 0, impactTime); } if(psDel->psStats->subType == FEAT_SKYSCRAPER) { pos.x = psDel->pos.x; pos.z = psDel->pos.y; pos.y = psDel->pos.z; addEffect(&pos, EFFECT_DESTRUCTION, DESTRUCTION_TYPE_SKYSCRAPER, true, psDel->sDisplay.imd, 0, impactTime); initPerimeterSmoke(psDel->sDisplay.imd, pos); shakeStart(250); // small shake } /* Then a sequence of effects */ pos.x = psDel->pos.x; pos.z = psDel->pos.y; pos.y = map_Height(pos.x,pos.z); addEffect(&pos, EFFECT_DESTRUCTION, DESTRUCTION_TYPE_FEATURE, false, NULL, 0, impactTime); //play sound // ffs gj if(psDel->psStats->subType == FEAT_SKYSCRAPER) { audio_PlayStaticTrack( psDel->pos.x, psDel->pos.y, ID_SOUND_BUILDING_FALL ); } else { audio_PlayStaticTrack( psDel->pos.x, psDel->pos.y, ID_SOUND_EXPLOSION ); } } if (psDel->psStats->subType == FEAT_SKYSCRAPER) { // ----- Flip all the tiles under the skyscraper to a rubble tile // smoke effect should disguise this happening StructureBounds b = getStructureBounds(psDel); for (int breadth = 0; breadth < b.size.y; ++breadth) { for (int width = 0; width < b.size.x; ++width) { MAPTILE *psTile = mapTile(b.map.x + width, b.map.y + breadth); // stops water texture changing for underwater features if (terrainType(psTile) != TER_WATER) { if (terrainType(psTile) != TER_CLIFFFACE) { /* Clear feature bits */ psTile->texture = TileNumber_texture(psTile->texture) | RUBBLE_TILE; auxClearBlocking(b.map.x + width, b.map.y + breadth, AUXBITS_ALL); } else { /* This remains a blocking tile */ psTile->psObject = NULL; auxClearBlocking(b.map.x + width, b.map.y + breadth, AIR_BLOCKED); // Shouldn't remain blocking for air units, however. psTile->texture = TileNumber_texture(psTile->texture) | BLOCKING_RUBBLE_TILE; } } } } } removeFeature(psDel); psDel->died = impactTime; return true; }
// this is able to handle multiple weapon graphics now // removed mountRotation,they get such stuff from psObj directly now static bool displayCompObj(DROID *psDroid, bool bButton, const glm::mat4 &viewMatrix) { iIMDShape *psMoveAnim, *psStillAnim; SDWORD iConnector; PROPULSION_STATS *psPropStats; SDWORD pieFlag, iPieData; PIELIGHT brightness; UDWORD colour; UBYTE i; bool didDrawSomething = false; glm::mat4 modelMatrix(1.f); if (graphicsTime - psDroid->timeLastHit < GAME_TICKS_PER_SEC / 4 && psDroid->lastHitWeapon == WSC_ELECTRONIC && !gamePaused()) { colour = getPlayerColour(rand() % MAX_PLAYERS); } else { colour = getPlayerColour(psDroid->player); } /* get propulsion stats */ psPropStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION]; ASSERT_OR_RETURN(didDrawSomething, psPropStats != nullptr, "invalid propulsion stats pointer"); //set pieflag for button object or ingame object if (bButton) { pieFlag = pie_BUTTON; brightness = WZCOL_WHITE; } else { pieFlag = pie_SHADOW; brightness = pal_SetBrightness(psDroid->illumination); // NOTE: Beware of transporters that are offscreen, on a mission! We should *not* be checking tiles at this point in time! if (!isTransporter(psDroid) && !missionIsOffworld()) { MAPTILE *psTile = worldTile(psDroid->pos.x, psDroid->pos.y); if (psTile->jammerBits & alliancebits[psDroid->player]) { pieFlag |= pie_ECM; } } } /* set default components transparent */ if (psDroid->asBits[COMP_PROPULSION] == 0) { pieFlag |= pie_TRANSLUCENT; iPieData = DEFAULT_COMPONENT_TRANSLUCENCY; } else { iPieData = 0; } if (!bButton && psPropStats->propulsionType == PROPULSION_TYPE_PROPELLOR) { // FIXME: change when adding submarines to the game modelMatrix *= glm::translate(glm::vec3(0.f, -world_coord(1) / 2.3f, 0.f)); } iIMDShape *psShapeProp = (leftFirst ? getLeftPropulsionIMD(psDroid) : getRightPropulsionIMD(psDroid)); if (psShapeProp) { if (pie_Draw3DShape(psShapeProp, 0, colour, brightness, pieFlag, iPieData, viewMatrix * modelMatrix)) { didDrawSomething = true; } } /* set default components transparent */ if (psDroid->asBits[COMP_BODY] == 0) { pieFlag |= pie_TRANSLUCENT; iPieData = DEFAULT_COMPONENT_TRANSLUCENCY; } else { pieFlag &= ~pie_TRANSLUCENT; iPieData = 0; } /* Get the body graphic now*/ iIMDShape *psShapeBody = BODY_IMD(psDroid, psDroid->player); if (psShapeBody) { iIMDShape *strImd = psShapeBody; if (psDroid->droidType == DROID_PERSON) { modelMatrix *= glm::scale(glm::vec3(.75f)); // FIXME - hideous....!!!! } if (strImd->objanimpie[psDroid->animationEvent]) { strImd = psShapeBody->objanimpie[psDroid->animationEvent]; } glm::mat4 viewModelMatrix = viewMatrix * modelMatrix; while (strImd) { if (drawShape(psDroid, strImd, colour, brightness, pieFlag, iPieData, viewModelMatrix)) { didDrawSomething = true; } strImd = strImd->next; } } /* Render animation effects based on movement or lack thereof, if any */ psMoveAnim = asBodyStats[psDroid->asBits[COMP_BODY]].ppMoveIMDList[psDroid->asBits[COMP_PROPULSION]]; psStillAnim = asBodyStats[psDroid->asBits[COMP_BODY]].ppStillIMDList[psDroid->asBits[COMP_PROPULSION]]; glm::mat4 viewModelMatrix = viewMatrix * modelMatrix; if (!bButton && psMoveAnim && psDroid->sMove.Status != MOVEINACTIVE) { if (pie_Draw3DShape(psMoveAnim, getModularScaledGraphicsTime(psMoveAnim->animInterval, psMoveAnim->numFrames), colour, brightness, pie_ADDITIVE, 200, viewModelMatrix)) { didDrawSomething = true; } } else if (!bButton && psStillAnim) // standing still { if (pie_Draw3DShape(psStillAnim, getModularScaledGraphicsTime(psStillAnim->animInterval, psStillAnim->numFrames), colour, brightness, 0, 0, viewModelMatrix)) { didDrawSomething = true; } } //don't change the screen coords of an object if drawing it in a button if (!bButton) { /* set up all the screen coords stuff - need to REMOVE FROM THIS LOOP */ calcScreenCoords(psDroid, viewModelMatrix); } /* set default components transparent */ if (psDroid->asWeaps[0].nStat == 0 && psDroid->asBits[COMP_SENSOR] == 0 && psDroid->asBits[COMP_ECM] == 0 && psDroid->asBits[COMP_BRAIN] == 0 && psDroid->asBits[COMP_REPAIRUNIT] == 0 && psDroid->asBits[COMP_CONSTRUCT] == 0) { pieFlag |= pie_TRANSLUCENT; iPieData = DEFAULT_COMPONENT_TRANSLUCENCY; } else { pieFlag &= ~pie_TRANSLUCENT; iPieData = 0; } if (psShapeBody && psShapeBody->nconnectors) { /* vtol weapons attach to connector 2 (underneath); * all others to connector 1 */ /* VTOL's now skip the first 5 connectors(0 to 4), VTOL's use 5,6,7,8 etc now */ if (psPropStats->propulsionType == PROPULSION_TYPE_LIFT && psDroid->droidType == DROID_WEAPON) { iConnector = VTOL_CONNECTOR_START; } else { iConnector = 0; } switch (psDroid->droidType) { case DROID_DEFAULT: case DROID_TRANSPORTER: case DROID_SUPERTRANSPORTER: case DROID_CYBORG: case DROID_CYBORG_SUPER: case DROID_WEAPON: case DROID_COMMAND: // command droids have a weapon to store all the graphics /* Get the mounting graphic - we've already moved to the right position Allegedly - all droids will have a mount graphic so this shouldn't fall on it's arse......*/ /* Double check that the weapon droid actually has any */ for (i = 0; i < psDroid->numWeaps; i++) { if ((psDroid->asWeaps[i].nStat > 0 || psDroid->droidType == DROID_DEFAULT) && psShapeBody->connectors) { Rotation rot = getInterpolatedWeaponRotation(psDroid, i, graphicsTime); glm::mat4 localModelMatrix = modelMatrix; //to skip number of VTOL_CONNECTOR_START ground unit connectors if (iConnector < VTOL_CONNECTOR_START) { localModelMatrix *= glm::translate(glm::vec3(psShapeBody->connectors[i].xzy())); } else { localModelMatrix *= glm::translate(glm::vec3(psShapeBody->connectors[iConnector + i].xzy())); } localModelMatrix *= glm::rotate(UNDEG(-rot.direction), glm::vec3(0.f, 1.f, 0.f)); /* vtol weapons inverted */ if (iConnector >= VTOL_CONNECTOR_START) { //this might affect gun rotation localModelMatrix *= glm::rotate(UNDEG(65536 / 2), glm::vec3(0.f, 0.f, 1.f)); } /* Get the mount graphic */ iIMDShape *psShape = WEAPON_MOUNT_IMD(psDroid, i); int recoilValue = getRecoil(psDroid->asWeaps[i]); localModelMatrix *= glm::translate(glm::vec3(0.f, 0.f, recoilValue / 3.f)); /* Draw it */ if (psShape) { if (pie_Draw3DShape(psShape, 0, colour, brightness, pieFlag, iPieData, viewMatrix * localModelMatrix)) { didDrawSomething = true; } } localModelMatrix *= glm::translate(glm::vec3(0, 0, recoilValue)); /* translate for weapon mount point */ if (psShape && psShape->nconnectors) { localModelMatrix *= glm::translate(glm::vec3(psShape->connectors->xzy())); } /* vtol weapons inverted */ if (iConnector >= VTOL_CONNECTOR_START) { //pitch the barrel down localModelMatrix *= glm::rotate(UNDEG(-rot.pitch), glm::vec3(1.f, 0.f, 0.f)); } else { //pitch the barrel up localModelMatrix *= glm::rotate(UNDEG(rot.pitch), glm::vec3(1.f, 0.f, 0.f)); } /* Get the weapon (gun?) graphic */ psShape = WEAPON_IMD(psDroid, i); // We have a weapon so we draw it and a muzzle flash from weapon connector if (psShape) { glm::mat4 localViewModelMatrix = viewMatrix * localModelMatrix; if (pie_Draw3DShape(psShape, 0, colour, brightness, pieFlag, iPieData, localViewModelMatrix)) { didDrawSomething = true; } drawMuzzleFlash(psDroid->asWeaps[i], psShape, MUZZLE_FLASH_PIE(psDroid, i), brightness, pieFlag, iPieData, localViewModelMatrix); } } } break; case DROID_SENSOR: case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: case DROID_ECM: case DROID_REPAIR: case DROID_CYBORG_REPAIR: { Rotation rot = getInterpolatedWeaponRotation(psDroid, 0, graphicsTime); iIMDShape *psShape = nullptr; iIMDShape *psMountShape = nullptr; switch (psDroid->droidType) { default: ASSERT(false, "Bad component type"); break; case DROID_SENSOR: psMountShape = SENSOR_MOUNT_IMD(psDroid, psDroid->player); /* Get the sensor graphic, assuming it's there */ psShape = SENSOR_IMD(psDroid, psDroid->player); break; case DROID_CONSTRUCT: case DROID_CYBORG_CONSTRUCT: psMountShape = CONSTRUCT_MOUNT_IMD(psDroid, psDroid->player); /* Get the construct graphic assuming it's there */ psShape = CONSTRUCT_IMD(psDroid, psDroid->player); break; case DROID_ECM: psMountShape = ECM_MOUNT_IMD(psDroid, psDroid->player); /* Get the ECM graphic assuming it's there.... */ psShape = ECM_IMD(psDroid, psDroid->player); break; case DROID_REPAIR: case DROID_CYBORG_REPAIR: psMountShape = REPAIR_MOUNT_IMD(psDroid, psDroid->player); /* Get the Repair graphic assuming it's there.... */ psShape = REPAIR_IMD(psDroid, psDroid->player); break; } /* Get the mounting graphic - we've already moved to the right position Allegedly - all droids will have a mount graphic so this shouldn't fall on it's arse......*/ //sensor and cyborg and ecm uses connectors[0] glm::mat4 localModelMatrix = modelMatrix; /* vtol weapons inverted */ if (iConnector >= VTOL_CONNECTOR_START) { //this might affect gun rotation localModelMatrix *= glm::rotate(UNDEG(65536 / 2), glm::vec3(0.f, 0.f, 1.f)); } localModelMatrix *= glm::translate(glm::vec3(psShapeBody->connectors[0].xzy())); localModelMatrix *= glm::rotate(UNDEG(-rot.direction), glm::vec3(0.f, 1.f, 0.f)); /* Draw it */ if (psMountShape) { if (pie_Draw3DShape(psMountShape, 0, colour, brightness, pieFlag, iPieData, viewMatrix * localModelMatrix)) { didDrawSomething = true; } } /* translate for construct mount point if cyborg */ if (cyborgDroid(psDroid) && psMountShape && psMountShape->nconnectors) { localModelMatrix *= glm::translate(glm::vec3(psMountShape->connectors[0].xzy())); } /* Draw it */ if (psShape) { if (pie_Draw3DShape(psShape, 0, colour, brightness, pieFlag, iPieData, viewMatrix * localModelMatrix)) { didDrawSomething = true; } // In repair droid case only: if ((psDroid->droidType == DROID_REPAIR || psDroid->droidType == DROID_CYBORG_REPAIR) && psShape->nconnectors && psDroid->action == DACTION_DROIDREPAIR) { Spacetime st = interpolateObjectSpacetime(psDroid, graphicsTime); localModelMatrix *= glm::translate(glm::vec3(psShape->connectors[0].xzy())); localModelMatrix *= glm::translate(glm::vec3(0.f, -20.f, 0.f)); psShape = getImdFromIndex(MI_FLAME); /* Rotate for droid */ localModelMatrix *= glm::rotate(UNDEG(st.rot.direction), glm::vec3(0.f, 1.f, 0.f)); localModelMatrix *= glm::rotate(UNDEG(-st.rot.pitch), glm::vec3(1.f, 0.f, 0.f)); localModelMatrix *= glm::rotate(UNDEG(-st.rot.roll), glm::vec3(0.f, 0.f, 1.f)); //rotate Y localModelMatrix *= glm::rotate(UNDEG(rot.direction), glm::vec3(0.f, 1.f, 0.f)); localModelMatrix *= glm::rotate(UNDEG(-player.r.y), glm::vec3(0.f, 1.f, 0.f)); localModelMatrix *= glm::rotate(UNDEG(-player.r.x), glm::vec3(1.f, 0.f, 0.f)); if (pie_Draw3DShape(psShape, getModularScaledGraphicsTime(psShape->animInterval, psShape->numFrames), 0, brightness, pie_ADDITIVE, 140, viewMatrix * localModelMatrix)) { didDrawSomething = true; } // localModelMatrix *= glm::rotate(UNDEG(player.r.x), glm::vec3(1.f, 0.f, 0.f)); // Not used? // localModelMatrix *= glm::rotate(UNDEG(player.r.y), glm::vec3(0.f, 1.f, 0.f)); // Not used? } } break; } case DROID_PERSON: // no extra mounts for people break; default: ASSERT(!"invalid droid type", "Whoa! Weirdy type of droid found in drawComponentObject!!!"); break; } } /* set default components transparent */ if (psDroid->asBits[COMP_PROPULSION] == 0) { pieFlag |= pie_TRANSLUCENT; iPieData = DEFAULT_COMPONENT_TRANSLUCENCY; } else { pieFlag &= ~pie_TRANSLUCENT; iPieData = 0; } // now render the other propulsion side psShapeProp = (leftFirst ? getRightPropulsionIMD(psDroid) : getLeftPropulsionIMD(psDroid)); if (psShapeProp) { if (pie_Draw3DShape(psShapeProp, 0, colour, brightness, pieFlag, iPieData, viewModelMatrix)) // Safe to use viewModelMatrix because modelView has not been changed since it was calculated { didDrawSomething = true; } } return didDrawSomething; }
// Colour Lookups // use col = MAX_PLAYERS for anycolour (see multiint.c) bool setPlayerColour(UDWORD player, UDWORD col) { ASSERT_OR_RETURN(false, player < MAX_PLAYERS && col < MAX_PLAYERS, "Bad colour setting"); NetPlay.players[player].colour = col; return true; }
// //////////////////////////////////////////////////////////////////////////// // receive droid creation information from other players bool recvDroid(NETQUEUE queue) { DROID_TEMPLATE* pT; DROID* psDroid; uint8_t player; uint32_t id; Position pos; uint32_t templateID; bool haveInitialOrders; INITIAL_DROID_ORDERS initialOrders; NETbeginDecode(queue, GAME_DEBUG_ADD_DROID); { NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETuint32_t(&templateID); NETbool(&haveInitialOrders); if (haveInitialOrders) { NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } pT = IdToTemplate(templateID, player); } NETend(); if (!getDebugMappingStatus()) { debug(LOG_WARNING, "Failed to add droid for player %u.", NetPlay.players[queue.index].position); return false; } ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player %u", player); debug(LOG_LIFE, "<=== getting Droid from %u id of %u ",player,id); if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight)) { debug(LOG_ERROR, "Received bad droid position (%d, %d) from %d about p%d (%s)", (int)pos.x, (int)pos.y, queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); return false; } // If we can not find the template ask for the entire droid instead if (!pT) { debug(LOG_ERROR, "Packet from %d refers to non-existent template %u, [%s : p%d]", queue.index, templateID, isHumanPlayer(player) ? "Human" : "AI", player); return false; } // Create that droid on this machine. psDroid = reallyBuildDroid(pT, pos, player, false); // If we were able to build the droid set it up if (psDroid) { psDroid->id = id; addDroid(psDroid, apsDroidLists); if (haveInitialOrders) { psDroid->secondaryOrder = initialOrders.secondaryOrder; psDroid->secondaryOrderPending = psDroid->secondaryOrder; orderDroidLoc(psDroid, DORDER_MOVE, initialOrders.moveToX, initialOrders.moveToY, ModeImmediate); cbNewDroid(IdToStruct(initialOrders.factoryId, ANYPLAYER), psDroid); } syncDebugDroid(psDroid, '+'); } else { debug(LOG_ERROR, "Packet from %d cannot create droid for p%d (%s)!", queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); #ifdef DEBUG CONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync")); #endif return false; } return true; }
/*! * Load shape levels recursively * \param ppFileData Pointer to the data (usualy read from a file) * \param FileDataEnd ??? * \param nlevels Number of levels to load * \return pointer to iFSDShape structure (or NULL on error) * \pre ppFileData loaded * \post s allocated */ static iIMDShape *_imd_load_level(const char **ppFileData, const char *FileDataEnd, int nlevels, int pieVersion) { const char *pTmp, *pFileData = *ppFileData; char buffer[PATH_MAX] = {'\0'}; int cnt = 0, n = 0, i; iIMDShape *s = NULL; if (nlevels == 0) { return NULL; } // Load optional MATERIALS directive pTmp = pFileData; // remember position i = sscanf(pFileData, "%255s %n", buffer, &cnt); ASSERT_OR_RETURN(NULL, i == 1, "Bad directive following LEVEL"); s = new iIMDShape; if (strcmp(buffer, "MATERIALS") == 0) { i = sscanf(pFileData, "%255s %f %f %f %f %f %f %f %f %f %f%n", buffer, &s->material[LIGHT_AMBIENT][0], &s->material[LIGHT_AMBIENT][1], &s->material[LIGHT_AMBIENT][2], &s->material[LIGHT_DIFFUSE][0], &s->material[LIGHT_DIFFUSE][1], &s->material[LIGHT_DIFFUSE][2], &s->material[LIGHT_SPECULAR][0], &s->material[LIGHT_SPECULAR][1], &s->material[LIGHT_SPECULAR][2], &s->shininess, &cnt); ASSERT_OR_RETURN(NULL, i == 11, "Bad MATERIALS directive"); pFileData += cnt; } else // use defaults { pFileData = pTmp; } if (sscanf(pFileData, "%255s %d%n", buffer, &s->npoints, &cnt) != 2) { debug(LOG_ERROR, "_imd_load_level(2): file corrupt"); return NULL; } pFileData += cnt; // load points ASSERT_OR_RETURN(NULL, strcmp(buffer, "POINTS") == 0, "Expecting 'POINTS' directive, got: %s", buffer); _imd_load_points( &pFileData, s ); if (sscanf(pFileData, "%255s %d%n", buffer, &s->npolys, &cnt) != 2) { debug(LOG_ERROR, "_imd_load_level(3): file corrupt"); return NULL; } pFileData += cnt; ASSERT_OR_RETURN(NULL, strcmp(buffer, "POLYGONS") == 0, "Expecting 'POLYGONS' directive, got: %s", buffer); _imd_load_polys( &pFileData, s, pieVersion); // NOW load optional stuff while (!AtEndOfFile(pFileData, FileDataEnd)) // check for end of file (give or take white space) { // Scans in the line ... if we don't get 2 parameters then quit if (sscanf(pFileData, "%255s %d%n", buffer, &n, &cnt) != 2) { break; } pFileData += cnt; // check for next level ... or might be a BSP - This should handle an imd if it has a BSP tree attached to it // might be "BSP" or "LEVEL" if (strcmp(buffer, "LEVEL") == 0) { debug(LOG_3D, "imd[_load_level] = npoints %d, npolys %d", s->npoints, s->npolys); s->next = _imd_load_level(&pFileData, FileDataEnd, nlevels - 1, pieVersion); } else if (strcmp(buffer, "CONNECTORS") == 0) { //load connector stuff s->nconnectors = n; _imd_load_connectors( &pFileData, s ); } else { debug(LOG_ERROR, "(_load_level) unexpected directive %s %d", buffer, n); break; } } // FINALLY, massage the data into what can stream directly to OpenGL glGenBuffers(VBO_COUNT, s->buffers); vertexCount = 0; for (int k = 0; k < MAX(1, s->numFrames); k++) { // Go through all polygons for each frame for (int i = 0; i < s->npolys; i++) { const iIMDPoly *pPolys = &s->polys[i]; // Do we already have the vertex data for this polygon? indices.append(addVertex(s, 0, pPolys, k)); indices.append(addVertex(s, 1, pPolys, k)); indices.append(addVertex(s, 2, pPolys, k)); } } glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_VERTEX]); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_NORMAL]); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(GLfloat), normals.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->buffers[VBO_INDEX]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(uint16_t), indices.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, s->buffers[VBO_TEXCOORD]); glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(GLfloat), texcoords.constData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind indices.resize(0); vertices.resize(0); texcoords.resize(0); normals.resize(0); *ppFileData = pFileData; return s; }
/** Search the global list of sensors for a possible target for psObj. */ static BASE_OBJECT *aiSearchSensorTargets(BASE_OBJECT *psObj, int weapon_slot, WEAPON_STATS *psWStats, UWORD *targetOrigin) { int longRange = proj_GetLongRange(psWStats); int tarDist = longRange * longRange; BOOL foundCB = false; int minDist = psWStats->minRange * psWStats->minRange; BASE_OBJECT *psSensor, *psTarget = NULL; if (targetOrigin) { *targetOrigin = ORIGIN_UNKNOWN; } for (psSensor = apsSensorList[0]; psSensor; psSensor = psSensor->psNextFunc) { BASE_OBJECT *psTemp = NULL; bool isCB = false; bool isRD = false; if (!aiCheckAlliances(psSensor->player, psObj->player)) { continue; } else if (psSensor->type == OBJ_DROID) { DROID *psDroid = (DROID *)psSensor; ASSERT_OR_RETURN(false, psDroid->droidType == DROID_SENSOR, "A non-sensor droid in a sensor list is non-sense"); psTemp = psDroid->psTarget; isCB = cbSensorDroid(psDroid); isRD = objRadarDetector((BASE_OBJECT *)psDroid); } else if (psSensor->type == OBJ_STRUCTURE) { STRUCTURE *psCStruct = (STRUCTURE *)psSensor; // skip incomplete structures if (psCStruct->status != SS_BUILT) { continue; } psTemp = psCStruct->psTarget[0]; isCB = structCBSensor(psCStruct); isRD = objRadarDetector((BASE_OBJECT *)psCStruct); } if (!psTemp || aiObjectIsProbablyDoomed(psTemp) || !validTarget(psObj, psTemp, 0) || aiCheckAlliances(psTemp->player, psObj->player)) { continue; } if (aiObjHasRange(psObj, psTemp, weapon_slot) && visibleObject(psSensor, psTemp, false)) { int distSq = objPosDiffSq(psTemp->pos, psObj->pos); // Need to be in range, prefer closer targets or CB targets if ((isCB > foundCB || (isCB == foundCB && distSq < tarDist)) && distSq > minDist) { tarDist = distSq; psTarget = psTemp; if (targetOrigin) { *targetOrigin = ORIGIN_SENSOR; } if (isCB) { if (targetOrigin) { *targetOrigin = ORIGIN_CB_SENSOR; } foundCB = true; // got CB target, drop everything and shoot! } else if (isRD) { if (targetOrigin) { *targetOrigin = ORIGIN_RADAR_DETECTOR; } } } } } return psTarget; }
// Read/compile/link shaders SHADER_MODE pie_LoadShader(const char *programName, const char *vertexPath, const char *fragmentPath, const std::vector<std::string> &uniformNames) { pie_internal::SHADER_PROGRAM program; GLint status; bool success = true; // Assume overall success char *buffer[2]; program.program = glCreateProgram(); glBindAttribLocation(program.program, 0, "vertex"); glBindAttribLocation(program.program, 1, "vertexTexCoord"); glBindAttribLocation(program.program, 2, "vertexColor"); ASSERT_OR_RETURN(SHADER_NONE, program.program, "Could not create shader program!"); *buffer = (char *)""; if (vertexPath) { success = false; // Assume failure before reading shader file if ((*(buffer + 1) = readShaderBuf(vertexPath))) { GLuint shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(shader, 2, (const char **)buffer, nullptr); glCompileShader(shader); // Check for compilation errors glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (!status) { debug(LOG_ERROR, "Vertex shader compilation has failed [%s]", vertexPath); printShaderInfoLog(LOG_ERROR, shader); } else { printShaderInfoLog(LOG_3D, shader); glAttachShader(program.program, shader); success = true; } if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glObjectLabel(GL_SHADER, shader, -1, vertexPath); } free(*(buffer + 1)); } } if (success && fragmentPath) { success = false; // Assume failure before reading shader file if ((*(buffer + 1) = readShaderBuf(fragmentPath))) { GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(shader, 2, (const char **)buffer, nullptr); glCompileShader(shader); // Check for compilation errors glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (!status) { debug(LOG_ERROR, "Fragment shader compilation has failed [%s]", fragmentPath); printShaderInfoLog(LOG_ERROR, shader); } else { printShaderInfoLog(LOG_3D, shader); glAttachShader(program.program, shader); success = true; } if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glObjectLabel(GL_SHADER, shader, -1, fragmentPath); } free(*(buffer + 1)); } } if (success) { glLinkProgram(program.program); // Check for linkage errors glGetProgramiv(program.program, GL_LINK_STATUS, &status); if (!status) { debug(LOG_ERROR, "Shader program linkage has failed [%s, %s]", vertexPath, fragmentPath); printProgramInfoLog(LOG_ERROR, program.program); success = false; } else { printProgramInfoLog(LOG_3D, program.program); } if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glObjectLabel(GL_PROGRAM, program.program, -1, programName); } } GLuint p = program.program; std::transform(uniformNames.begin(), uniformNames.end(), std::back_inserter(program.locations), [p](const std::string name) { return glGetUniformLocation(p, name.data()); }); getLocs(&program); glUseProgram(0); pie_internal::shaderProgram.push_back(program); return SHADER_MODE(pie_internal::shaderProgram.size() - 1); }
bool iV_loadImage_PNG(const char *fileName, iV_Image *image) { unsigned char PNGheader[PNG_BYTES_TO_CHECK]; PHYSFS_sint64 readSize; png_structp png_ptr = NULL; png_infop info_ptr = NULL; // Open file PHYSFS_file* fileHandle = PHYSFS_openRead(fileName); if (fileHandle == NULL) { debug(LOG_ERROR, "pie_PNGLoadFile: PHYSFS_openRead(%s) failed with error: %s\n", fileName, PHYSFS_getLastError()); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } // Read PNG header from file readSize = PHYSFS_read(fileHandle, PNGheader, 1, PNG_BYTES_TO_CHECK); if (readSize < PNG_BYTES_TO_CHECK) { debug(LOG_ERROR, "pie_PNGLoadFile: PHYSFS_read(%s) failed with error: %s\n", fileName, PHYSFS_getLastError()); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } // Verify the PNG header to be correct if (png_sig_cmp(PNGheader, 0, PNG_BYTES_TO_CHECK)) { debug(LOG_3D, "pie_PNGLoadMem: Did not recognize PNG header in %s", fileName); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { debug(LOG_3D, "pie_PNGLoadMem: Unable to create png struct"); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { debug(LOG_3D, "pie_PNGLoadMem: Unable to create png info struct"); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } // Set libpng's failure jump position to the if branch, // setjmp evaluates to false so the else branch will be executed at first if (setjmp(png_jmpbuf(png_ptr))) { debug(LOG_3D, "pie_PNGLoadMem: Error decoding PNG data in %s", fileName); PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); return false; } // Tell libpng how many byte we already read png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); /* Set up the input control */ png_set_read_fn(png_ptr, fileHandle, wzpng_read_data); // Most of the following transformations are seemingly not needed // Filler is, however, for an unknown reason required for tertilesc[23] /* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ // png_set_packing(png_ptr); /* More transformations to ensure we end up with 32bpp, 4 channel RGBA */ // png_set_gray_to_rgb(png_ptr); png_set_palette_to_rgb(png_ptr); png_set_tRNS_to_alpha(png_ptr); png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); // png_set_gray_1_2_4_to_8(png_ptr); png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); image->width = png_get_image_width(png_ptr, info_ptr); image->height = png_get_image_height(png_ptr, info_ptr); image->depth = png_get_channels(png_ptr, info_ptr); image->bmp = (unsigned char *)malloc(image->height * png_get_rowbytes(png_ptr, info_ptr)); { unsigned int i = 0; png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); for ( i = 0; i < png_get_image_height(png_ptr, info_ptr); i++ ) memcpy( image->bmp + (png_get_rowbytes(png_ptr, info_ptr) * i), row_pointers[i], png_get_rowbytes(png_ptr, info_ptr) ); } PNGReadCleanup(&info_ptr, &png_ptr, fileHandle); ASSERT_OR_RETURN(false, image->depth > 3, "Unsupported image depth (%d) found. We only support 3 (RGB) or 4 (ARGB)", image->depth); return true; }
/*! * Load shape level polygons * \param ppFileData Pointer to the data (usualy read from a file) * \param s Pointer to shape level * \return false on error (memory allocation failure/bad file format), true otherwise * \pre ppFileData loaded * \pre s allocated * \pre s->npolys set * \post s->polys allocated (iFSDPoly * s->npolys) * \post s->pindex allocated for each poly */ static bool _imd_load_polys(const char **ppFileData, iIMDShape *s, int pieVersion) { const char *pFileData = *ppFileData; unsigned int i, j; iIMDPoly *poly; s->numFrames = 0; s->animInterval = 0; s->polys = (iIMDPoly *)malloc(sizeof(iIMDPoly) * s->npolys); if (s->polys == NULL) { debug(LOG_ERROR, "(_load_polys) Out of memory (polys)"); return false; } for (i = 0, poly = s->polys; i < s->npolys; i++, poly++) { unsigned int flags, npnts; int cnt; if (sscanf(pFileData, "%x %u%n", &flags, &npnts, &cnt) != 2) { debug(LOG_ERROR, "(_load_polys) [poly %u] error loading flags and npoints", i); } pFileData += cnt; poly->flags = flags; ASSERT_OR_RETURN(false, npnts == 3, "Invalid polygon size (%d)", npnts); if (sscanf(pFileData, "%d %d %d%n", &poly->pindex[0], &poly->pindex[1], &poly->pindex[2], &cnt) != 3) { debug(LOG_ERROR, "failed reading triangle, point %d", i); return false; } pFileData += cnt; // calc poly normal { Vector3f p0, p1, p2; //assumes points already set p0.x = s->points[poly->pindex[0]].x; p0.y = s->points[poly->pindex[0]].y; p0.z = s->points[poly->pindex[0]].z; p1.x = s->points[poly->pindex[1]].x; p1.y = s->points[poly->pindex[1]].y; p1.z = s->points[poly->pindex[1]].z; p2.x = s->points[poly->pindex[2]].x; p2.y = s->points[poly->pindex[2]].y; p2.z = s->points[poly->pindex[2]].z; poly->normal = pie_SurfaceNormal3fv(p0, p1, p2); } // texture coord routine if (poly->flags & iV_IMD_TEX) { int nFrames, framesPerLine, frame, pbRate; float tWidth, tHeight; if (poly->flags & iV_IMD_TEXANIM) { if (sscanf(pFileData, "%d %d %f %f%n", &nFrames, &pbRate, &tWidth, &tHeight, &cnt) != 4) { debug(LOG_ERROR, "(_load_polys) [poly %u] error reading texanim data", i); return false; } pFileData += cnt; ASSERT(tWidth > 0.0001f, "%s: texture width = %f", GetLastResourceFilename(), tWidth); ASSERT(tHeight > 0.f, "%s: texture height = %f (width=%f)", GetLastResourceFilename(), tHeight, tWidth); ASSERT(nFrames > 1, "%s: animation frames = %d", GetLastResourceFilename(), nFrames); ASSERT(pbRate > 0, "%s: animation interval = %d ms", GetLastResourceFilename(), pbRate); /* Must have same number of frames and same playback rate for all polygons */ if (s->numFrames == 0) { s->numFrames = nFrames; s->animInterval = pbRate; } else { ASSERT(s->numFrames == nFrames, "%s: varying number of frames within one PIE level: %d != %d", GetLastResourceFilename(), nFrames, s->numFrames); ASSERT(s->animInterval == pbRate, "%s: varying animation intervals within one PIE level: %d != %d", GetLastResourceFilename(), pbRate, s->animInterval); } poly->texAnim.x = tWidth; poly->texAnim.y = tHeight; if (pieVersion != PIE_FLOAT_VER) { poly->texAnim.x /= OLD_TEXTURE_SIZE_FIX; poly->texAnim.y /= OLD_TEXTURE_SIZE_FIX; } framesPerLine = 1 / poly->texAnim.x; } else { nFrames = 1; framesPerLine = 1; pbRate = 1; tWidth = 0.f; tHeight = 0.f; poly->texAnim.x = 0; poly->texAnim.y = 0; } poly->texCoord = (Vector2f *)malloc(sizeof(*poly->texCoord) * nFrames * 3); ASSERT_OR_RETURN(false, poly->texCoord, "Out of memory allocating texture coordinates"); for (j = 0; j < 3; j++) { float VertexU, VertexV; if (sscanf(pFileData, "%f %f%n", &VertexU, &VertexV, &cnt) != 2) { debug(LOG_ERROR, "(_load_polys) [poly %u] error reading tex outline", i); return false; } pFileData += cnt; if (pieVersion != PIE_FLOAT_VER) { VertexU /= OLD_TEXTURE_SIZE_FIX; VertexV /= OLD_TEXTURE_SIZE_FIX; } for (frame = 0; frame < nFrames; frame++) { const float uFrame = (frame % framesPerLine) * poly->texAnim.x; const float vFrame = (frame / framesPerLine) * poly->texAnim.y; Vector2f *c = &poly->texCoord[frame * 3 + j]; c->x = VertexU + uFrame; c->y = VertexV + vFrame; } } } else { ASSERT_OR_RETURN(false, !(poly->flags & iV_IMD_TEXANIM), "Polygons with texture animation must have textures!"); poly->texCoord = NULL; } } *ppFileData = pFileData; return true; }
bool setPlayerName(int player, const char *sName) { ASSERT_OR_RETURN(false, player < MAX_PLAYERS && player >= 0, "Player index (%u) out of range", player); sstrcpy(playerName[player], sName); return true; }
// ppFileData is incremented to the end of the file on exit! static iIMDShape *iV_ProcessIMD(const char **ppFileData, const char *FileDataEnd) { const char *pFileName = GetLastResourceFilename(); // Last loaded filename const char *pFileData = *ppFileData; char buffer[PATH_MAX], texfile[PATH_MAX], normalfile[PATH_MAX], specfile[PATH_MAX]; int cnt, nlevels; iIMDShape *shape; UDWORD level; int32_t imd_version; uint32_t imd_flags; bool bTextured = false; GLuint shader = 0; memset(normalfile, 0, sizeof(normalfile)); memset(specfile, 0, sizeof(specfile)); if (sscanf(pFileData, "%255s %d%n", buffer, &imd_version, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad version: (%s)", pFileName, buffer); assert(false); return NULL; } pFileData += cnt; if (strcmp(PIE_NAME, buffer) != 0) { debug(LOG_ERROR, "iV_ProcessIMD %s not an IMD file (%s %d)", pFileName, buffer, imd_version); return NULL; } //Now supporting version PIE_VER and PIE_FLOAT_VER files if (imd_version != PIE_VER && imd_version != PIE_FLOAT_VER) { debug(LOG_ERROR, "iV_ProcessIMD %s version %d not supported", pFileName, imd_version); return NULL; } // Read flag if (sscanf(pFileData, "%255s %x%n", buffer, &imd_flags, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad flags: %s", pFileName, buffer); return NULL; } pFileData += cnt; /* This can be either texture or levels */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s expecting TEXTURE or LEVELS: %s", pFileName, buffer); return NULL; } pFileData += cnt; // get texture page if specified if (strncmp(buffer, "TEXTURE", 7) == 0) { int i, pwidth, pheight; char ch, texType[PATH_MAX]; /* the first parameter for textures is always ignored; which is why we ignore * nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { texfile[i] = ch; } texfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "iV_ProcessIMD %s texture info corrupt: %s", pFileName, buffer); return NULL; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "iV_ProcessIMD %s: only png textures supported", pFileName); return NULL; } sstrcat(texfile, ".png"); if (sscanf(pFileData, "%d %d%n", &pwidth, &pheight, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad texture size: %s", pFileName, buffer); return NULL; } pFileData += cnt; /* Now read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad levels info: %s", pFileName, buffer); return NULL; } pFileData += cnt; bTextured = true; } if (strncmp(buffer, "NORMALMAP", 9) == 0) { char ch, texType[PATH_MAX]; int i; /* the first parameter for textures is always ignored; which is why we ignore * nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { normalfile[i] = ch; } normalfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "iV_ProcessIMD %s normal map info corrupt: %s", pFileName, buffer); return NULL; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "iV_ProcessIMD %s: only png normal maps supported", pFileName); return NULL; } sstrcat(normalfile, ".png"); /* Now read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad levels info: %s", pFileName, buffer); return NULL; } pFileData += cnt; } if (strncmp(buffer, "SPECULARMAP", 11) == 0) { char ch, texType[PATH_MAX]; int i; /* the first parameter for textures is always ignored; which is why we ignore nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { specfile[i] = ch; } specfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "%s specular map info corrupt: %s", pFileName, buffer); return NULL; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "%s: only png specular maps supported", pFileName); return NULL; } sstrcat(specfile, ".png"); /* Try -again- to read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad levels info: %s", pFileName, buffer); return NULL; } pFileData += cnt; } // DEPRECATED SHADERS DIRECTIVE! Has been moved into levels block now. Remove me later. if (strncmp(buffer, "SHADERS", 7) == 0) { char vertex[PATH_MAX], fragment[PATH_MAX]; /* the first parameter for "textures" is always ignored; which is why we ignore nlevels read in above */ pFileData++; if (sscanf(pFileData, "%255s %255s%n", vertex, fragment, &cnt) != 2) { debug(LOG_ERROR, "%s shader corrupt: %s", pFileName, buffer); return NULL; } pFileData += cnt; shader = pie_LoadShader(pFileName, vertex, fragment); /* Try -yet again- to read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "iV_ProcessIMD %s bad levels info: %s", pFileName, buffer); return NULL; } pFileData += cnt; } if (strncmp(buffer, "LEVELS", 6) != 0) { debug(LOG_ERROR, "iV_ProcessIMD: expecting 'LEVELS' directive (%s)", buffer); return NULL; } /* Read first LEVEL directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &level, &cnt) != 2) { debug(LOG_ERROR, "(_load_level) file corrupt -J"); return NULL; } pFileData += cnt; if (strncmp(buffer, "LEVEL", 5) != 0) { debug(LOG_ERROR, "iV_ProcessIMD(2): expecting 'LEVEL' directive (%s)", buffer); return NULL; } shape = _imd_load_level(&pFileData, FileDataEnd, nlevels, imd_version); if (shape == NULL) { debug(LOG_ERROR, "iV_ProcessIMD %s unsuccessful", pFileName); return NULL; } // assign shader to all levels, if old deprecated SHADERS directive used. FIXME remove this later. for (iIMDShape *psShape = shape; shader && psShape != NULL; psShape = psShape->next) { shape->shaderProgram = shader; } // load texture page if specified if (bTextured) { int texpage = iV_GetTexture(texfile); int normalpage = iV_TEX_INVALID; int specpage = iV_TEX_INVALID; ASSERT_OR_RETURN(NULL, texpage >= 0, "%s could not load tex page %s", pFileName, texfile); if (normalfile[0] != '\0') { debug(LOG_TEXTURE, "Loading normal map %s for %s", normalfile, pFileName); normalpage = iV_GetTexture(normalfile); ASSERT_OR_RETURN(NULL, normalpage >= 0, "%s could not load tex page %s", pFileName, normalfile); } if (specfile[0] != '\0') { debug(LOG_TEXTURE, "Loading specular map %s for %s", specfile, pFileName); specpage = iV_GetTexture(specfile); ASSERT_OR_RETURN(NULL, specpage >= 0, "%s could not load tex page %s", pFileName, specfile); } // assign tex pages and flags to all levels for (iIMDShape *psShape = shape; psShape != NULL; psShape = psShape->next) { psShape->texpage = texpage; psShape->normalpage = normalpage; psShape->specularpage = specpage; psShape->flags = imd_flags; } // check if model should use team colour mask if (imd_flags & iV_IMD_TCMASK) { int texpage_mask; pie_MakeTexPageTCMaskName(texfile); sstrcat(texfile, ".png"); texpage_mask = iV_GetTexture(texfile); ASSERT_OR_RETURN(shape, texpage_mask >= 0, "%s could not load tcmask %s", pFileName, texfile); // Propagate settings through levels for (iIMDShape *psShape = shape; psShape != NULL; psShape = psShape->next) { psShape->tcmaskpage = texpage_mask; } } } *ppFileData = pFileData; return shape; }
// Get values from a base object bool scrBaseObjGet(UDWORD index) { INTERP_TYPE type = VAL_VOID; BASE_OBJECT *psObj; DROID *psDroid; STRUCTURE *psStruct; FEATURE *psFeature; if (!stackPopParams(1, ST_BASEOBJECT, &psObj)) { debug(LOG_ERROR, "scrBaseObjGet: stackPopParams failed"); return false; } // Check this is a valid pointer ASSERT_OR_RETURN(false, psObj, "Passed a NULL pointer to a base object"); ASSERT_OR_RETURN(false, psObj->type == OBJ_DROID || psObj->type == OBJ_STRUCTURE || psObj->type == OBJ_FEATURE, "Invalid object %p of type %d", psObj, psObj->type); // set the type and return value switch (index) { case OBJID_POSX: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->pos.x; break; case OBJID_POSY: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->pos.y; break; case OBJID_POSZ: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->pos.z; break; case OBJID_ID: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->id; break; case OBJID_PLAYER: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->player; break; case OBJID_TYPE: type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)psObj->type; break; case OBJID_ORDER: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: order only valid for a droid"); return false; } type = VAL_INT; scrFunctionResult.v.ival = ((DROID *)psObj)->order.type; if (scrFunctionResult.v.ival == DORDER_GUARD && ((DROID *)psObj)->order.psObj == NULL) { scrFunctionResult.v.ival = DORDER_NONE; } break; //new member variable case OBJID_ACTION: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: action only valid for a droid"); return false; } type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->action; break; //new member variable - if droid is selected (humans only) case OBJID_SELECTED: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: selected only valid for a droid"); return false; } type = VAL_BOOL; scrFunctionResult.v.bval = (SDWORD)((DROID *)psObj)->selected; break; case OBJID_STRUCTSTATTYPE: if (psObj->type == OBJ_STRUCTURE) { type = VAL_INT; scrFunctionResult.v.ival = ((STRUCTURE *)psObj)->pStructureType->type; } else { debug(LOG_ERROR, ".stattype is only supported by Structures"); return false; } break; case OBJID_ORDERX: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: order only valid for a droid"); return false; } type = VAL_INT; scrFunctionResult.v.ival = ((DROID *)psObj)->order.pos.x; break; case OBJID_ORDERY: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: order only valid for a droid"); return false; } type = VAL_INT; scrFunctionResult.v.ival = ((DROID *)psObj)->order.pos.y; break; case OBJID_DROIDTYPE: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: droidType only valid for a droid"); return false; } type = VAL_INT; scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->droidType; break; case OBJID_CLUSTERID: if (psObj->type == OBJ_FEATURE) { debug(LOG_ERROR, "scrBaseObjGet: clusterID not valid for features"); return false; } type = VAL_INT; scrFunctionResult.v.ival = clustGetClusterID(psObj); break; case OBJID_HEALTH: switch (psObj->type) { case OBJ_DROID: psDroid = (DROID *)psObj; type = VAL_INT; scrFunctionResult.v.ival = psDroid->body * 100 / psDroid->originalBody; break; case OBJ_FEATURE: psFeature = (FEATURE *)psObj; type = VAL_INT; if (psFeature->psStats->damageable) { scrFunctionResult.v.ival = psFeature->body * 100 / psFeature->psStats->body; } else { scrFunctionResult.v.ival = 100; } break; case OBJ_STRUCTURE: psStruct = (STRUCTURE *)psObj; type = VAL_INT; //val = psStruct->body * 100 / psStruct->baseBodyPoints; scrFunctionResult.v.ival = psStruct->body * 100 / structureBody(psStruct); break; default: break; } break; case OBJID_BODY: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: body only valid for a droid"); return false; } type = (INTERP_TYPE)ST_BODY; scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->asBits[COMP_BODY].nStat; break; case OBJID_PROPULSION: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: propulsion only valid for a droid"); return false; } type = (INTERP_TYPE)ST_PROPULSION; scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->asBits[COMP_PROPULSION].nStat; break; case OBJID_WEAPON: //TODO: only returns first weapon now type = (INTERP_TYPE)ST_WEAPON; switch (psObj->type) { case OBJ_DROID: if (((DROID *)psObj)->asWeaps[0].nStat == 0) { scrFunctionResult.v.ival = 0; }else{ scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->asWeaps[0].nStat; } break; case OBJ_STRUCTURE: if (((STRUCTURE *)psObj)->numWeaps == 0 || ((STRUCTURE *)psObj)->asWeaps[0].nStat == 0) { scrFunctionResult.v.ival = 0; }else{ scrFunctionResult.v.ival = (SDWORD)((STRUCTURE *)psObj)->asWeaps[0].nStat; } break; default: //only droids and structures can have a weapon debug(LOG_ERROR, "scrBaseObjGet: weapon only valid for droids and structures" ); return false; break; } break; case OBJID_STRUCTSTAT: //droid.stat - now returns the type of structure a truck is building for droids if (psObj->type == OBJ_STRUCTURE) { type = (INTERP_TYPE)ST_STRUCTURESTAT; scrFunctionResult.v.ival = ((STRUCTURE *)psObj)->pStructureType - asStructureStats; } else if (psObj->type == OBJ_DROID) { type = (INTERP_TYPE)ST_STRUCTURESTAT; scrFunctionResult.v.ival = ((DROID *)psObj)->order.psStats - asStructureStats; } else //Nothing else supported { debug(LOG_ERROR, "scrBaseObjGet(): .stat only valid for structures and droids"); return false; } break; case OBJID_TARGET: //added object->psTarget if (psObj->type == OBJ_STRUCTURE) { type = (INTERP_TYPE)ST_BASEOBJECT; scrFunctionResult.v.oval = ((STRUCTURE *)psObj)->psTarget[0]; } else if (psObj->type == OBJ_DROID) { type = (INTERP_TYPE)ST_BASEOBJECT; scrFunctionResult.v.oval = ((DROID *)psObj)->order.psObj; } else //Nothing else supported { debug(LOG_ERROR, "scrBaseObjGet(): .target only valid for structures and droids"); return false; } break; case OBJID_GROUP: if (psObj->type != OBJ_DROID) { debug(LOG_ERROR, "scrBaseObjGet: group only valid for a droid"); return false; } type = (INTERP_TYPE)ST_GROUP; scrFunctionResult.v.oval = ((DROID *)psObj)->psGroup; break; case OBJID_HITPOINTS: type = VAL_INT; switch (psObj->type) { case OBJ_DROID: scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->body; break; case OBJ_STRUCTURE: scrFunctionResult.v.ival = (SDWORD)((STRUCTURE *)psObj)->body; break; case OBJ_FEATURE: scrFunctionResult.v.ival = (SDWORD)((FEATURE *)psObj)->body; break; default: debug(LOG_ERROR, "scrBaseObjGet: unknown object type"); return false; break; } break; case OBJID_ORIG_HITPOINTS: type = VAL_INT; switch (psObj->type) { case OBJ_DROID: scrFunctionResult.v.ival = (SDWORD)((DROID *)psObj)->originalBody; break; case OBJ_STRUCTURE: scrFunctionResult.v.ival = (SDWORD)structureBody((STRUCTURE *)psObj); break; case OBJ_FEATURE: scrFunctionResult.v.ival = ((FEATURE *)psObj)->psStats->body; break; default: debug(LOG_ERROR, "scrBaseObjGet: unknown object type"); return false; break; } break; default: debug(LOG_ERROR, "scrBaseObjGet: unknown variable index"); return false; break; } // Return the value if (!stackPushResult(type, &scrFunctionResult)) { debug(LOG_ERROR, "scrBaseObjGet: stackPushResult() failed"); return false; } return true; }
/* This returns true if the key went from being down to being up this frame */ bool keyReleased(KEY_CODE code) { ASSERT_OR_RETURN(false, code < KEY_MAXSCAN, "Invalid keycode of %d!", (int)code); return ((aKeyState[code].state == KEY_RELEASED) || (aKeyState[code].state == KEY_PRESSRELEASE)); }
/// default value load routine bool scrValDefLoad(INTERP_VAL *psVal, WzConfig &ini) { DROID *psCDroid; SDWORD index, members; UDWORD id; LEVEL_DATASET *psLevel; DROID_GROUP *psGroup = NULL; switch ((unsigned)psVal->type) // Unsigned cast to suppress compiler warnings due to enum abuse. { case ST_INTMESSAGE: if (ini.contains("data")) { psVal->v.oval = (void*)getViewData(ini.value("data").toString().toAscii().constData()); } else { psVal->v.oval = NULL; } break; case ST_BASEOBJECT: case ST_DROID: case ST_STRUCTURE: case ST_FEATURE: if (ini.contains("data")) { psVal->v.oval = (void*)getBaseObjFromId(ini.value("data").toInt()); } else { psVal->v.oval = NULL; } break; case ST_BASESTATS: case ST_COMPONENT: break; case ST_STRUCTURESTAT: index = 0; if (ini.contains("data")) { index = getStructStatFromName(ini.value("data").toString().toAscii().constData()); if (index == -1) { debug( LOG_FATAL, "Could not find stat"); index = 0; } } psVal->v.ival = index; break; case ST_FEATURESTAT: index = 0; if (ini.contains("data")) { index = getFeatureStatFromName(ini.value("data").toString().toAscii().constData()); if (index == -1) { debug( LOG_FATAL, "Could not find stat"); index = 0; } } psVal->v.ival = index; break; case ST_BODY: index = getCompFromResName(COMP_BODY, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find body component"); index = 0; } psVal->v.ival = index; break; case ST_PROPULSION: index = getCompFromResName(COMP_PROPULSION, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find propulsion component"); index = 0; } psVal->v.ival = index; break; case ST_ECM: index = getCompFromResName(COMP_ECM, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find ECM component"); index = 0; } psVal->v.ival = index; break; case ST_SENSOR: index = getCompFromResName(COMP_SENSOR, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find sensor component"); index = 0; } psVal->v.ival = index; break; case ST_CONSTRUCT: index = getCompFromResName(COMP_CONSTRUCT, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find constructor component"); index = 0; } psVal->v.ival = index; break; case ST_WEAPON: index = getCompFromResName(COMP_WEAPON, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find weapon"); index = 0; } psVal->v.ival = index; break; case ST_REPAIR: index = getCompFromResName(COMP_REPAIRUNIT, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find repair component"); index = 0; } psVal->v.ival = index; break; case ST_BRAIN: index = getCompFromResName(COMP_BRAIN, ini.value("data").toString().toAscii().constData()); if (index == -1) { debug(LOG_FATAL, "Could not find repair brain"); index = 0; } psVal->v.ival = index; break; case ST_TEMPLATE: psVal->v.oval = NULL; if (ini.contains("data")) { // FIXME: Ugh. Find a better way to show full template info psVal->v.oval = (void*)IdToTemplate(ini.value("data").toInt(), ANYPLAYER); if ((DROID_TEMPLATE*)(psVal->v.oval) == NULL) { debug(LOG_FATAL, "Could not find template %d", ini.value("data").toInt()); } } break; case ST_TEXTSTRING: psVal->v.sval = NULL; if (ini.contains("data")) { psVal->v.sval = strdup(ini.value("data").toString().toAscii().constData()); } break; case ST_LEVEL: psVal->v.sval = NULL; if (ini.contains("data")) { psLevel = levFindDataSet(ini.value("data").toString().toAscii().constData()); if (psLevel == NULL) { debug(LOG_FATAL, "Could not find level dataset"); } psVal->v.sval = psLevel->pName; } break; case ST_RESEARCH: psVal->v.oval = NULL; if (ini.contains("data")) { QString research = ini.value("data").toString(); if (!research.isEmpty()) { psVal->v.oval = (void*)getResearch(research.toUtf8().constData()); ASSERT_OR_RETURN(false, psVal->v.oval, "Could not find research %s", research.toUtf8().constData()); } } break; case ST_GROUP: if (psVal->v.oval == NULL) { DROID_GROUP *tmp = grpCreate(); tmp->add(NULL); psVal->v.oval = tmp; } psGroup = (DROID_GROUP *)(psVal->v.oval); members = ini.value("members", 0).toInt(); if (psGroup && members > 0) { QStringList droids = ini.value("data").toStringList(); // load the retreat data psGroup->sRunData.sPos = ini.vector2i("runpos"); psGroup->sRunData.forceLevel = ini.value("forceLevel").toInt(); psGroup->sRunData.leadership = ini.value("leadership").toInt(); psGroup->sRunData.healthLevel = ini.value("healthLevel").toInt(); // load the droids while (members > 0) { id = droids.takeLast().toInt(); psCDroid = (DROID *)getBaseObjFromId(id); if (!psCDroid) { debug(LOG_ERROR, "Could not find object id %d", id); } else { ((DROID_GROUP*)(psVal->v.oval))->add(psCDroid); } members--; } } break; case ST_SOUND: // find audio id // don't use sound if it's disabled if (audio_Disabled()) { psVal->v.ival = NO_SOUND; break; } index = audio_GetTrackID(ini.value("data").toString().toAscii().constData()); if (index == SAMPLE_NOT_FOUND) { // find empty id and set track vals QString soundname = ini.value("data").toString(); index = audio_SetTrackVals(soundname.toAscii().constData(), false, 100, 1800); if (!index) // this is a NON fatal error. { // We can't find filename of the sound for some reason. debug(LOG_ERROR, "Sound ID not available %s not found", soundname.toAscii().constData()); break; } } psVal->v.ival = index; break; case ST_STRUCTUREID: case ST_DROIDID: default: // just set the contents directly psVal->v.ival = ini.value("data").toInt(); break; } return true; }
/** Load the research stats */ bool loadResearch(WzConfig &ini) { ASSERT(ini.isAtDocumentRoot(), "WzConfig instance is in the middle of traversal"); std::vector<WzString> list = ini.childGroups(); PLAYER_RESEARCH dummy; memset(&dummy, 0, sizeof(dummy)); std::vector< std::vector<WzString> > preResearch; preResearch.resize(list.size()); for (size_t inc = 0; inc < list.size(); ++inc) { // HACK FIXME: the code assumes we have empty PLAYER_RESEARCH entries to throw around for (auto &j : asPlayerResList) { j.push_back(dummy); } ini.beginGroup(list[inc]); RESEARCH research; research.index = inc; research.name = ini.string("name"); research.id = list[inc]; //check the name hasn't been used already ASSERT_OR_RETURN(false, checkResearchName(&research, inc), "Research name '%s' used already", getName(&research)); research.ref = REF_RESEARCH_START + inc; research.results = ini.json("results", nlohmann::json::array()); //set subGroup icon WzString subGroup = ini.value("subgroupIconID", "").toWzString(); if (subGroup.compare("") != 0) { research.subGroup = setIconID(subGroup.toUtf8().c_str(), getName(&research)); } else { research.subGroup = NO_RESEARCH_ICON; } //set key topic unsigned int keyTopic = ini.value("keyTopic", 0).toUInt(); ASSERT(keyTopic <= 1, "Invalid keyTopic for research topic - '%s' ", getName(&research)); if (keyTopic <= 1) { research.keyTopic = ini.value("keyTopic", 0).toUInt(); } else { research.keyTopic = 0; } //set tech code UBYTE techCode = ini.value("techCode", 0).toUInt(); ASSERT(techCode <= 1, "Invalid tech code for research topic - '%s' ", getName(&research)); if (techCode == 0) { research.techCode = TC_MAJOR; } else { research.techCode = TC_MINOR; } //set the iconID WzString iconID = ini.value("iconID", "").toWzString(); if (iconID.compare("") != 0) { research.iconID = setIconID(iconID.toUtf8().c_str(), getName(&research)); } else { research.iconID = NO_RESEARCH_ICON; } //get the IMDs used in the interface WzString statID = ini.value("statID", "").toWzString(); research.psStat = nullptr; if (statID.compare("") != 0) { //try find the structure stat with given name research.psStat = getCompStatsFromName(statID); ASSERT_OR_RETURN(false, research.psStat, "Could not find stats for %s research %s", statID.toUtf8().c_str(), getName(&research)); } WzString imdName = ini.value("imdName", "").toWzString(); if (imdName.compare("") != 0) { research.pIMD = modelGet(imdName); ASSERT(research.pIMD != nullptr, "Cannot find the research PIE '%s' for record '%s'", imdName.toUtf8().data(), getName(&research)); } WzString imdName2 = ini.value("imdName2", "").toWzString(); if (imdName2.compare("") != 0) { research.pIMD2 = modelGet(imdName2); ASSERT(research.pIMD2 != nullptr, "Cannot find the 2nd research '%s' PIE for record '%s'", imdName2.toUtf8().data(), getName(&research)); } WzString msgName = ini.value("msgName", "").toWzString(); if (msgName.compare("") != 0) { //check its a major tech code ASSERT(research.techCode == TC_MAJOR, "This research should not have a message associated with it, '%s' the message will be ignored!", getName(&research)); if (research.techCode == TC_MAJOR) { research.pViewData = getViewData(msgName); } } //set the researchPoints unsigned int resPoints = ini.value("researchPoints", 0).toUInt(); ASSERT_OR_RETURN(false, resPoints <= UWORD_MAX, "Research Points too high for research topic - '%s' ", getName(&research)); research.researchPoints = resPoints; //set the research power unsigned int resPower = ini.value("researchPower", 0).toUInt(); ASSERT_OR_RETURN(false, resPower <= UWORD_MAX, "Research Power too high for research topic - '%s' ", getName(&research)); research.researchPower = resPower; //remember research pre-requisites for futher checking preResearch[inc] = ini.value("requiredResearch").toWzStringList(); //set components results std::vector<WzString> compResults = ini.value("resultComponents").toWzStringList(); for (size_t j = 0; j < compResults.size(); j++) { WzString compID = compResults[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp != nullptr) { research.componentResults.push_back(pComp); } else { ASSERT(false, "Invalid item '%s' in list of result components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } } //set replaced components std::vector<WzString> replacedComp = ini.value("replacedComponents").toWzStringList(); for (size_t j = 0; j < replacedComp.size(); j++) { //read pair of components oldComponent:newComponent std::vector<WzString> pair = replacedComp[j].split(":"); ASSERT(pair.size() == 2, "Invalid item '%s' in list of replaced components of research '%s'. Required format: 'oldItem:newItem, item1:item2'", replacedComp[j].toUtf8().c_str(), getName(&research)); if (pair.size() != 2) { continue; //skip invalid entries } WzString oldCompID = pair[0].trimmed(); WzString newCompID = pair[1].trimmed(); COMPONENT_STATS *oldComp = getCompStatsFromName(oldCompID); if (oldComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", oldCompID.toUtf8().c_str(), getName(&research)); continue; } COMPONENT_STATS *newComp = getCompStatsFromName(newCompID); if (newComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", newCompID.toUtf8().c_str(), getName(&research)); continue; } RES_COMP_REPLACEMENT replItem; replItem.pOldComponent = oldComp; replItem.pNewComponent = newComp; research.componentReplacement.push_back(replItem); } //set redundant components std::vector<WzString> redComp = ini.value("redComponents").toWzStringList(); for (size_t j = 0; j < redComp.size(); j++) { WzString compID = redComp[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of redundant components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } else { research.pRedArtefacts.push_back(pComp); } } //set result structures std::vector<WzString> resStruct = ini.value("resultStructures").toWzStringList(); for (size_t j = 0; j < resStruct.size(); j++) { WzString strucID = resStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID); ASSERT(structIndex >= 0, "Invalid item '%s' in list of result structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructureResults.push_back(structIndex); } } //set required structures std::vector<WzString> reqStruct = ini.value("requiredStructures").toWzStringList(); for (size_t j = 0; j < reqStruct.size(); j++) { WzString strucID = reqStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of required structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructList.push_back(structIndex); } } //set redundant structures std::vector<WzString> redStruct = ini.value("redStructures").toWzStringList(); for (size_t j = 0; j < redStruct.size(); j++) { WzString strucID = redStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of redundant structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pRedStructs.push_back(structIndex); } } asResearch.push_back(research); ini.endGroup(); } //Load and check research pre-requisites (need do it AFTER loading research items) for (size_t inc = 0; inc < asResearch.size(); inc++) { std::vector<WzString> &preRes = preResearch[inc]; for (size_t j = 0; j < preRes.size(); j++) { WzString resID = preRes[j].trimmed(); RESEARCH *preResItem = getResearch(resID.toUtf8().c_str()); ASSERT(preResItem != nullptr, "Invalid item '%s' in list of pre-requisites of research '%s' ", resID.toUtf8().c_str(), getName(&asResearch[inc])); if (preResItem != nullptr) { asResearch[inc].pPRList.push_back(preResItem->index); } } } return true; }
bool loadFunctionStats(const char *pFunctionData, UDWORD bufferSize) { //array of functions pointers for each load function static const LoadFunction pLoadFunction[NUMFUNCTIONS] = { loadProduction, loadProductionUpgradeFunction, loadResearchFunction, loadResearchUpgradeFunction, loadPowerGenFunction, loadResourceFunction, loadRepairDroidFunction, loadWeaponUpgradeFunction, loadWallFunction, loadStructureUpgradeFunction, loadWallDefenceUpgradeFunction, loadPowerUpgradeFunction, loadRepairUpgradeFunction, loadDroidRepairUpgradeFunction, loadDroidECMUpgradeFunction, loadDroidBodyUpgradeFunction, loadDroidSensorUpgradeFunction, loadDroidConstUpgradeFunction, loadReArmFunction, loadReArmUpgradeFunction, }; const unsigned int totalFunctions = numCR(pFunctionData, bufferSize); UDWORD i; FUNCTION_TYPE type; char FunctionType[MAX_STR_LENGTH]; FUNCTION **pStartList; //allocate storage for the Function pointer array asFunctions = (FUNCTION **) malloc(totalFunctions * sizeof(FUNCTION *)); pStartList = asFunctions; //initialise the storage memset(asFunctions, 0, totalFunctions * sizeof(FUNCTION *)); numFunctions = 0; for (i = 0; i < totalFunctions; i++) { //read the data into the storage - the data is delimeted using comma's FunctionType[0] = '\0'; sscanf(pFunctionData, "%255[^,'\r\n]", FunctionType); type = functionType(FunctionType); ASSERT_OR_RETURN(false, type != NUMFUNCTIONS, "Function type not found"); pFunctionData += (strlen(FunctionType) + 1); if (!(pLoadFunction[type](pFunctionData))) { return false; } //increment the pointer to the start of the next record pFunctionData = strchr(pFunctionData, '\n') + 1; } //set the function list pointer to the start asFunctions = pStartList; return true; }