static void LoadMapObject(MapObject *m, json_t *node) { memset(m, 0, sizeof *m); m->Idx = -1; LoadInt(&m->Idx, node, "Index"); m->Name = GetString(node, "Name"); LoadPic(&m->Normal.Pic, node, "Pic", "OldPic"); LoadPic(&m->Wreck.Pic, node, "WreckPic", "OldWreckPic"); if (m->Normal.Pic) { // Default offset: centered X, align bottom of tile and sprite m->Normal.Offset = Vec2iNew( -m->Normal.Pic->size.x / 2, TILE_HEIGHT / 2 - m->Normal.Pic->size.y); LoadVec2i(&m->Normal.Offset, node, "Offset"); } if (m->Wreck.Pic) { m->Wreck.Offset = Vec2iScaleDiv(m->Wreck.Pic->size, -2); LoadVec2i(&m->Wreck.Offset, node, "WreckOffset"); } // Default tile size m->Size = Vec2iNew(TILE_WIDTH, TILE_HEIGHT); LoadVec2i(&m->Size, node, "Size"); LoadInt(&m->Health, node, "Health"); LoadBulletGuns(&m->DestroyGuns, node, "DestroyGuns"); json_t *flagsNode = json_find_first_label(node, "Flags"); if (flagsNode != NULL && flagsNode->child != NULL) { for (json_t *flagNode = flagsNode->child->child; flagNode; flagNode = flagNode->next) { m->Flags |= 1 << StrPlacementFlag(flagNode->text); } } // Special types JSON_UTILS_LOAD_ENUM(m->Type, node, "Type", StrMapObjectType); switch (m->Type) { case MAP_OBJECT_TYPE_NORMAL: // Do nothing break; case MAP_OBJECT_TYPE_PICKUP_SPAWNER: { char *tmp = GetString(node, "Pickup"); m->u.PickupClass = StrPickupClass(tmp); CFREE(tmp); } break; default: CASSERT(false, "unknown error"); break; } }
static void LoadStaticExit(Mission *m, json_t *node, char *name) { json_t *exitNode = json_find_first_label(node, name); if (!exitNode || !exitNode->child) { return; } exitNode = exitNode->child; LoadVec2i(&m->u.Static.Exit.Start, exitNode, "Start"); LoadVec2i(&m->u.Static.Exit.End, exitNode, "End"); }
static bool TryLoadStaticMap(Mission *m, json_t *node, int version) { CArrayInit(&m->u.Static.Tiles, sizeof(unsigned short)); if (version == 1) { // JSON array json_t *tiles = json_find_first_label(node, "Tiles"); if (!tiles || !tiles->child) { return false; } tiles = tiles->child; for (tiles = tiles->child; tiles; tiles = tiles->next) { unsigned short n = (unsigned short)atoi(tiles->text); CArrayPushBack(&m->u.Static.Tiles, &n); } } else { // CSV string char *tileCSV = GetString(node, "Tiles"); char *pch = strtok(tileCSV, ","); while (pch != NULL) { unsigned short n = (unsigned short)atoi(pch); CArrayPushBack(&m->u.Static.Tiles, &n); pch = strtok(NULL, ","); } CFREE(tileCSV); } CArrayInit(&m->u.Static.Items, sizeof(MapObjectPositions)); LoadStaticItems(m, node, "StaticItems", version); if (version < 13) { LoadStaticItems(m, node, "StaticWrecks", version); } LoadStaticCharacters(m, node, "StaticCharacters"); LoadStaticObjectives(m, node, "StaticObjectives"); LoadStaticKeys(m, node, "StaticKeys"); LoadVec2i(&m->u.Static.Start, node, "Start"); LoadStaticExit(m, node, "Exit"); return true; }
static void LoadBullet( BulletClass *b, json_t *node, const BulletClass *defaultBullet) { memset(b, 0, sizeof *b); if (defaultBullet != NULL) { memcpy(b, defaultBullet, sizeof *b); if (defaultBullet->HitSound.Object != NULL) { CSTRDUP(b->HitSound.Object, defaultBullet->HitSound.Object); } if (defaultBullet->HitSound.Flesh != NULL) { CSTRDUP(b->HitSound.Flesh, defaultBullet->HitSound.Flesh); } if (defaultBullet->HitSound.Wall != NULL) { CSTRDUP(b->HitSound.Wall, defaultBullet->HitSound.Wall); } // TODO: enable default bullet guns? memset(&b->Falling.DropGuns, 0, sizeof b->Falling.DropGuns); memset(&b->OutOfRangeGuns, 0, sizeof b->OutOfRangeGuns); memset(&b->HitGuns, 0, sizeof b->HitGuns); memset(&b->ProximityGuns, 0, sizeof b->ProximityGuns); } char *tmp; LoadStr(&b->Name, node, "Name"); if (json_find_first_label(node, "Pic")) { json_t *pic = json_find_first_label(node, "Pic")->child; tmp = GetString(pic, "Type"); b->CPic.Type = StrPicType(tmp); CFREE(tmp); bool picLoaded = false; switch (b->CPic.Type) { case PICTYPE_NORMAL: tmp = GetString(pic, "Pic"); b->CPic.u.Pic = PicManagerGetPic(&gPicManager, tmp); CFREE(tmp); picLoaded = b->CPic.u.Pic != NULL; break; case PICTYPE_DIRECTIONAL: tmp = GetString(pic, "Sprites"); b->CPic.u.Sprites = &PicManagerGetSprites(&gPicManager, tmp)->pics; CFREE(tmp); picLoaded = b->CPic.u.Sprites != NULL; break; case PICTYPE_ANIMATED: // fallthrough case PICTYPE_ANIMATED_RANDOM: tmp = GetString(pic, "Sprites"); b->CPic.u.Animated.Sprites = &PicManagerGetSprites(&gPicManager, tmp)->pics; CFREE(tmp); LoadInt(&b->CPic.u.Animated.Count, pic, "Count"); LoadInt(&b->CPic.u.Animated.TicksPerFrame, pic, "TicksPerFrame"); // Set safe default ticks per frame 1; // if 0 then this leads to infinite loop when animating b->CPic.u.Animated.TicksPerFrame = MAX( b->CPic.u.Animated.TicksPerFrame, 1); picLoaded = b->CPic.u.Animated.Sprites != NULL; break; default: CASSERT(false, "unknown pic type"); break; } b->CPic.UseMask = true; b->CPic.u1.Mask = colorWhite; if (json_find_first_label(pic, "Mask")) { tmp = GetString(pic, "Mask"); b->CPic.u1.Mask = StrColor(tmp); CFREE(tmp); } else if (json_find_first_label(pic, "Tint")) { b->CPic.UseMask = false; json_t *tint = json_find_first_label(pic, "Tint")->child->child; b->CPic.u1.Tint.h = atof(tint->text); tint = tint->next; b->CPic.u1.Tint.s = atof(tint->text); tint = tint->next; b->CPic.u1.Tint.v = atof(tint->text); } if ((json_find_first_label(pic, "OldPic") && ConfigGetBool(&gConfig, "Graphics.OriginalPics")) || !picLoaded) { int oldPic = PIC_UZIBULLET; LoadInt(&oldPic, pic, "OldPic"); b->CPic.Type = PICTYPE_NORMAL; b->CPic.u.Pic = PicManagerGetFromOld(&gPicManager, oldPic); } } LoadVec2i(&b->ShadowSize, node, "ShadowSize"); LoadInt(&b->Delay, node, "Delay"); if (json_find_first_label(node, "Speed")) { LoadInt(&b->SpeedLow, node, "Speed"); b->SpeedHigh = b->SpeedLow; } if (json_find_first_label(node, "SpeedLow")) { LoadInt(&b->SpeedLow, node, "SpeedLow"); } if (json_find_first_label(node, "SpeedHigh")) { LoadInt(&b->SpeedHigh, node, "SpeedHigh"); } b->SpeedLow = MIN(b->SpeedLow, b->SpeedHigh); b->SpeedHigh = MAX(b->SpeedLow, b->SpeedHigh); LoadBool(&b->SpeedScale, node, "SpeedScale"); LoadInt(&b->Friction, node, "Friction"); if (json_find_first_label(node, "Range")) { LoadInt(&b->RangeLow, node, "Range"); b->RangeHigh = b->RangeLow; } if (json_find_first_label(node, "RangeLow")) { LoadInt(&b->RangeLow, node, "RangeLow"); } if (json_find_first_label(node, "RangeHigh")) { LoadInt(&b->RangeHigh, node, "RangeHigh"); } b->RangeLow = MIN(b->RangeLow, b->RangeHigh); b->RangeHigh = MAX(b->RangeLow, b->RangeHigh); LoadInt(&b->Power, node, "Power"); LoadVec2i(&b->Size, node, "Size"); if (json_find_first_label(node, "Special")) { tmp = GetString(node, "Special"); b->Special = StrSpecialDamage(tmp); CFREE(tmp); } LoadBool(&b->HurtAlways, node, "HurtAlways"); LoadBool(&b->Persists, node, "Persists"); if (json_find_first_label(node, "Spark")) { tmp = GetString(node, "Spark"); b->Spark = StrParticleClass(&gParticleClasses, tmp); CFREE(tmp); } if (json_find_first_label(node, "HitSounds")) { json_t *hitSounds = json_find_first_label(node, "HitSounds")->child; CFREE(b->HitSound.Object); b->HitSound.Object = NULL; LoadStr(&b->HitSound.Object, hitSounds, "Object"); CFREE(b->HitSound.Flesh); b->HitSound.Flesh = NULL; LoadStr(&b->HitSound.Flesh, hitSounds, "Flesh"); CFREE(b->HitSound.Wall); b->HitSound.Wall = NULL; LoadStr(&b->HitSound.Wall, hitSounds, "Wall"); } LoadBool(&b->WallBounces, node, "WallBounces"); LoadBool(&b->HitsObjects, node, "HitsObjects"); if (json_find_first_label(node, "Falling")) { json_t *falling = json_find_first_label(node, "Falling")->child; LoadInt(&b->Falling.GravityFactor, falling, "GravityFactor"); LoadBool(&b->Falling.FallsDown, falling, "FallsDown"); LoadBool(&b->Falling.DestroyOnDrop, falling, "DestroyOnDrop"); LoadBool(&b->Falling.Bounces, falling, "Bounces"); } LoadInt(&b->SeekFactor, node, "SeekFactor"); LoadBool(&b->Erratic, node, "Erratic"); b->node = node; }
static void LoadBullet( BulletClass *b, json_t *node, const BulletClass *defaultBullet) { memset(b, 0, sizeof *b); if (defaultBullet != NULL) { memcpy(b, defaultBullet, sizeof *b); if (defaultBullet->Name != NULL) { CSTRDUP(b->Name, defaultBullet->Name); } if (defaultBullet->HitSound.Object != NULL) { CSTRDUP(b->HitSound.Object, defaultBullet->HitSound.Object); } if (defaultBullet->HitSound.Flesh != NULL) { CSTRDUP(b->HitSound.Flesh, defaultBullet->HitSound.Flesh); } if (defaultBullet->HitSound.Wall != NULL) { CSTRDUP(b->HitSound.Wall, defaultBullet->HitSound.Wall); } // TODO: enable default bullet guns? memset(&b->Falling.DropGuns, 0, sizeof b->Falling.DropGuns); memset(&b->OutOfRangeGuns, 0, sizeof b->OutOfRangeGuns); memset(&b->HitGuns, 0, sizeof b->HitGuns); memset(&b->ProximityGuns, 0, sizeof b->ProximityGuns); } char *tmp; tmp = NULL; LoadStr(&tmp, node, "Name"); if (tmp != NULL) { CFREE(b->Name); b->Name = tmp; } if (json_find_first_label(node, "Pic")) { CPicLoadJSON(&b->CPic, json_find_first_label(node, "Pic")->child); } LoadVec2i(&b->ShadowSize, node, "ShadowSize"); LoadInt(&b->Delay, node, "Delay"); if (json_find_first_label(node, "Speed")) { LoadInt(&b->SpeedLow, node, "Speed"); b->SpeedHigh = b->SpeedLow; } LoadInt(&b->SpeedLow, node, "SpeedLow"); LoadInt(&b->SpeedHigh, node, "SpeedHigh"); b->SpeedLow = MIN(b->SpeedLow, b->SpeedHigh); b->SpeedHigh = MAX(b->SpeedLow, b->SpeedHigh); LoadBool(&b->SpeedScale, node, "SpeedScale"); LoadInt(&b->Friction, node, "Friction"); if (json_find_first_label(node, "Range")) { LoadInt(&b->RangeLow, node, "Range"); b->RangeHigh = b->RangeLow; } LoadInt(&b->RangeLow, node, "RangeLow"); LoadInt(&b->RangeHigh, node, "RangeHigh"); b->RangeLow = MIN(b->RangeLow, b->RangeHigh); b->RangeHigh = MAX(b->RangeLow, b->RangeHigh); LoadInt(&b->Power, node, "Power"); LoadVec2i(&b->Size, node, "Size"); tmp = NULL; LoadStr(&tmp, node, "Special"); if (tmp != NULL) { b->Special = StrSpecialDamage(tmp); CFREE(tmp); } LoadBool(&b->HurtAlways, node, "HurtAlways"); LoadBool(&b->Persists, node, "Persists"); tmp = NULL; LoadStr(&tmp, node, "Spark"); if (tmp != NULL) { b->Spark = StrParticleClass(&gParticleClasses, tmp); CFREE(tmp); } if (json_find_first_label(node, "HitSounds")) { json_t *hitSounds = json_find_first_label(node, "HitSounds")->child; CFREE(b->HitSound.Object); b->HitSound.Object = NULL; LoadStr(&b->HitSound.Object, hitSounds, "Object"); CFREE(b->HitSound.Flesh); b->HitSound.Flesh = NULL; LoadStr(&b->HitSound.Flesh, hitSounds, "Flesh"); CFREE(b->HitSound.Wall); b->HitSound.Wall = NULL; LoadStr(&b->HitSound.Wall, hitSounds, "Wall"); } LoadBool(&b->WallBounces, node, "WallBounces"); LoadBool(&b->HitsObjects, node, "HitsObjects"); if (json_find_first_label(node, "Falling")) { json_t *falling = json_find_first_label(node, "Falling")->child; LoadInt(&b->Falling.GravityFactor, falling, "GravityFactor"); LoadBool(&b->Falling.FallsDown, falling, "FallsDown"); LoadBool(&b->Falling.DestroyOnDrop, falling, "DestroyOnDrop"); LoadBool(&b->Falling.Bounces, falling, "Bounces"); } LoadInt(&b->SeekFactor, node, "SeekFactor"); LoadBool(&b->Erratic, node, "Erratic"); b->node = node; LOG(LM_MAP, LL_DEBUG, "loaded bullet name(%s) shadowSize(%d, %d) delay(%d) speed(%d-%d)...", b->Name != NULL ? b->Name : "", b->ShadowSize.x, b->ShadowSize.y, b->Delay, b->SpeedLow, b->SpeedHigh); LOG(LM_MAP, LL_DEBUG, "...speedScale(%s) friction(%d) range(%d-%d) power(%d)...", b->SpeedScale ? "true" : "false", b->Friction, b->RangeLow, b->RangeHigh, b->Power); LOG(LM_MAP, LL_DEBUG, "...size(%d, %d) hurtAlways(%s) persists(%s) spark(%s)...", b->Size.x, b->Size.y, b->HurtAlways ? "true" : "false", b->Persists ? "true" : "false", b->Spark != NULL ? b->Spark->Name : ""); LOG(LM_MAP, LL_DEBUG, "...hitSounds(object(%s), flesh(%s), wall(%s)) wallBounces(%s)...", b->HitSound.Object != NULL ? b->HitSound.Object : "", b->HitSound.Flesh != NULL ? b->HitSound.Flesh : "", b->HitSound.Wall != NULL ? b->HitSound.Wall : "", b->WallBounces ? "true" : "false"); LOG(LM_MAP, LL_DEBUG, "...hitsObjects(%s) gravity(%d) fallsDown(%s) destroyOnDrop(%s)...", b->HitsObjects ? "true" : "false", b->Falling.GravityFactor, b->Falling.FallsDown ? "true" : "false", b->Falling.DestroyOnDrop ? "true" : "false"); LOG(LM_MAP, LL_DEBUG, "...dropGuns(%d) seekFactor(%d) erratic(%s)...", (int)b->Falling.DropGuns.size, b->SeekFactor, b->Erratic ? "true" : "false"); LOG(LM_MAP, LL_DEBUG, "...outOfRangeGuns(%d) hitGuns(%d) proximityGuns(%d)", (int)b->OutOfRangeGuns.size, (int)b->HitGuns.size, (int)b->ProximityGuns.size); }
static bool TryLoadMapObject(MapObject *m, json_t *node, const int version) { memset(m, 0, sizeof *m); m->Name = GetString(node, "Name"); // Pic json_t *normalNode = json_find_first_label(node, "Pic"); if (normalNode != NULL && normalNode->child != NULL) { if (version < 2) { CPicLoadNormal(&m->Pic, normalNode->child); } else { CPicLoadJSON(&m->Pic, normalNode->child); } // Pic required for map object if (!CPicIsLoaded(&m->Pic)) { LOG(LM_MAIN, LL_ERROR, "pic not found for map object(%s)", m->Name); goto bail; } } if (CPicIsLoaded(&m->Pic)) { // Default offset: centered X, align bottom of tile and sprite const Vec2i size = CPicGetSize(&m->Pic); m->Offset = Vec2iNew(-size.x / 2, TILE_HEIGHT / 2 - size.y); LoadVec2i(&m->Offset, node, "Offset"); } // Wreck if (version < 3) { // Assume old wreck pic is wreck json_t *wreckNode = json_find_first_label(node, "WreckPic"); if (wreckNode != NULL && wreckNode->child != NULL) { if (version < 2) { LoadStr(&m->Wreck, node, "WreckPic"); } else { LoadStr(&m->Wreck, wreckNode, "Pic"); } } } else { LoadStr(&m->Wreck, node, "Wreck"); } // Default tile size m->Size = TILE_SIZE; LoadVec2i(&m->Size, node, "Size"); LoadVec2i(&m->PosOffset, node, "PosOffset"); LoadInt(&m->Health, node, "Health"); LoadBulletGuns(&m->DestroyGuns, node, "DestroyGuns"); // Flags json_t *flagsNode = json_find_first_label(node, "Flags"); if (flagsNode != NULL && flagsNode->child != NULL) { for (json_t *flagNode = flagsNode->child->child; flagNode; flagNode = flagNode->next) { m->Flags |= 1 << StrPlacementFlag(flagNode->text); } } LoadBool(&m->DrawLast, node, "DrawLast"); // Special types JSON_UTILS_LOAD_ENUM(m->Type, node, "Type", StrMapObjectType); switch (m->Type) { case MAP_OBJECT_TYPE_NORMAL: // Do nothing break; case MAP_OBJECT_TYPE_PICKUP_SPAWNER: { char *tmp = GetString(node, "Pickup"); m->u.PickupClass = StrPickupClass(tmp); CFREE(tmp); } break; default: CASSERT(false, "unknown error"); break; } // DestroySpawn - pickups to spawn on destruction json_t *destroySpawnNode = json_find_first_label(node, "DestroySpawn"); if (destroySpawnNode != NULL && destroySpawnNode->child != NULL) { CArrayInit(&m->DestroySpawn, sizeof(MapObjectDestroySpawn)); for (json_t *dsNode = destroySpawnNode->child->child; dsNode; dsNode = dsNode->next) { MapObjectDestroySpawn mods; memset(&mods, 0, sizeof mods); JSON_UTILS_LOAD_ENUM(mods.Type, dsNode, "Type", StrPickupType); LoadDouble(&mods.SpawnChance, dsNode, "SpawnChance"); CArrayPushBack(&m->DestroySpawn, &mods); } } return true; bail: return false; }