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