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 bool DoDamageCharacter( const Vec2i pos, const Vec2i hitVector, const int power, const int flags, const int player, const int uid, const TTileItem *target, const special_damage_e special, const HitSounds *hitSounds, const bool allowFriendlyHitSound) { // Create events: hit, damage, score TActor *actor = CArrayGet(&gActors, target->id); CASSERT(actor->isInUse, "Cannot damage nonexistent player"); bool canHit = CanHitCharacter(flags, uid, actor); if (canHit) { GameEvent e; e.Type = GAME_EVENT_HIT_CHARACTER; e.u.HitCharacter.TargetId = actor->tileItem.id; e.u.HitCharacter.Special = special; GameEventsEnqueue(&gGameEvents, e); if (gConfig.Sound.Hits && hitSounds != NULL && !ActorIsImmune(actor, special) && (allowFriendlyHitSound || !ActorIsInvulnerable( actor, flags, player, gCampaign.Entry.Mode))) { GameEvent es; es.Type = GAME_EVENT_SOUND_AT; es.u.SoundAt.Sound = hitSounds->Flesh; es.u.SoundAt.Pos = pos; GameEventsEnqueue(&gGameEvents, es); } if (gConfig.Game.ShotsPushback) { GameEvent ei; ei.Type = GAME_EVENT_ACTOR_IMPULSE; ei.u.ActorImpulse.Id = actor->tileItem.id; ei.u.ActorImpulse.Vel = Vec2iScaleDiv( Vec2iScale(hitVector, power), SHOT_IMPULSE_DIVISOR); GameEventsEnqueue(&gGameEvents, ei); } if (CanDamageCharacter(flags, player, uid, actor, special)) { GameEvent e1; e1.Type = GAME_EVENT_DAMAGE_CHARACTER; e1.u.DamageCharacter.Power = power; e1.u.DamageCharacter.PlayerIndex = player; e1.u.DamageCharacter.TargetId = actor->tileItem.id; e1.u.DamageCharacter.TargetPlayerIndex = -1; if (actor->pData) { e1.u.DamageCharacter.TargetPlayerIndex = actor->pData->playerIndex; } GameEventsEnqueue(&gGameEvents, e1); if (gConfig.Game.Gore != GORE_NONE) { GameEvent eb; memset(&eb, 0, sizeof eb); eb.Type = GAME_EVENT_ADD_PARTICLE; eb.u.AddParticle.FullPos = Vec2iReal2Full(pos); eb.u.AddParticle.Z = 10 * Z_FACTOR; int bloodPower = power * 2; int bloodSize = 1; while (bloodPower > 0) { switch (bloodSize) { case 1: eb.u.AddParticle.Class = StrParticleClass(&gParticleClasses, "blood1"); break; case 2: eb.u.AddParticle.Class = StrParticleClass(&gParticleClasses, "blood2"); break; default: eb.u.AddParticle.Class = StrParticleClass(&gParticleClasses, "blood3"); break; } bloodSize++; if (bloodSize > 3) { bloodSize = 1; } if (gConfig.Game.ShotsPushback) { eb.u.AddParticle.Vel = Vec2iScaleDiv( Vec2iScale(hitVector, (rand() % 8 + 8) * power), 15 * SHOT_IMPULSE_DIVISOR); } else { eb.u.AddParticle.Vel = Vec2iScaleDiv( Vec2iScale(hitVector, rand() % 8 + 8), 20); } eb.u.AddParticle.Vel.x += (rand() % 128) - 64; eb.u.AddParticle.Vel.y += (rand() % 128) - 64; eb.u.AddParticle.Angle = RAND_DOUBLE(0, PI * 2); eb.u.AddParticle.DZ = (rand() % 6) + 6; eb.u.AddParticle.Spin = RAND_DOUBLE(-0.1, 0.1); GameEventsEnqueue(&gGameEvents, eb); switch (gConfig.Game.Gore) { case GORE_LOW: bloodPower /= 8; break; case GORE_MEDIUM: bloodPower /= 2; break; default: bloodPower = bloodPower * 7 / 8; break; } } } if (player >= 0 && power != 0) { // Calculate score based on // if they hit a penalty character GameEvent e2; e2.Type = GAME_EVENT_SCORE; e2.u.Score.PlayerIndex = player; if (actor->flags & FLAGS_PENALTY) { e2.u.Score.Score = PENALTY_MULTIPLIER * power; } else { e2.u.Score.Score = power; } GameEventsEnqueue(&gGameEvents, e2); } } } return canHit; }
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 void LoadGunDescription( GunDescription *g, json_t *node, const GunDescription *defaultGun) { memset(g, 0, sizeof *g); g->AmmoId = -1; if (defaultGun) { memcpy(g, defaultGun, sizeof *g); if (defaultGun->name) { CSTRDUP(g->name, defaultGun->name); } if (defaultGun->Description) { CSTRDUP(g->Description, defaultGun->Description); } g->MuzzleHeight /= Z_FACTOR; } char *tmp; if (json_find_first_label(node, "Pic")) { tmp = GetString(node, "Pic"); if (strcmp(tmp, "blaster") == 0) { g->pic = GUNPIC_BLASTER; } else if (strcmp(tmp, "knife") == 0) { g->pic = GUNPIC_KNIFE; } else { g->pic = -1; } CFREE(tmp); } if (json_find_first_label(node, "Icon")) { tmp = GetString(node, "Icon"); g->Icon = PicManagerGet(&gPicManager, tmp, -1); CFREE(tmp); } g->name = GetString(node, "Name"); if (json_find_first_label(node, "Description")) { g->Description = GetString(node, "Description"); } if (json_find_first_label(node, "Bullet")) { tmp = GetString(node, "Bullet"); g->Bullet = StrBulletClass(tmp); CFREE(tmp); } if (json_find_first_label(node, "Ammo")) { tmp = GetString(node, "Ammo"); g->AmmoId = StrAmmoId(tmp); CFREE(tmp); } LoadInt(&g->Cost, node, "Cost"); LoadInt(&g->Lock, node, "Lock"); LoadInt(&g->ReloadLead, node, "ReloadLead"); LoadSoundFromNode(&g->Sound, node, "Sound"); LoadSoundFromNode(&g->ReloadSound, node, "ReloadSound"); LoadSoundFromNode(&g->SwitchSound, node, "SwitchSound"); LoadInt(&g->SoundLockLength, node, "SoundLockLength"); LoadDouble(&g->Recoil, node, "Recoil"); LoadInt(&g->Spread.Count, node, "SpreadCount"); LoadDouble(&g->Spread.Width, node, "SpreadWidth"); LoadDouble(&g->AngleOffset, node, "AngleOffset"); LoadInt(&g->MuzzleHeight, node, "MuzzleHeight"); g->MuzzleHeight *= Z_FACTOR; if (json_find_first_label(node, "Elevation")) { LoadInt(&g->ElevationLow, node, "Elevation"); g->ElevationHigh = g->ElevationLow; } LoadInt(&g->ElevationLow, node, "ElevationLow"); LoadInt(&g->ElevationHigh, node, "ElevationHigh"); g->ElevationLow = MIN(g->ElevationLow, g->ElevationHigh); g->ElevationHigh = MAX(g->ElevationLow, g->ElevationHigh); if (json_find_first_label(node, "MuzzleFlashParticle")) { tmp = GetString(node, "MuzzleFlashParticle"); g->MuzzleFlash = StrParticleClass(&gParticleClasses, tmp); CFREE(tmp); } if (json_find_first_label(node, "Brass")) { tmp = GetString(node, "Brass"); g->Brass = StrParticleClass(&gParticleClasses, tmp); CFREE(tmp); } LoadBool(&g->CanShoot, node, "CanShoot"); LoadInt(&g->ShakeAmount, node, "ShakeAmount"); g->IsRealGun = true; }