Add rocket launcher (#268)

Change Friction to a single number
Enable Elevation JSON property for weapons
Enable multiple collisions for bullets
This commit is contained in:
Cong
2014-06-30 23:37:51 +10:00
parent c2488fb052
commit 5c3b3fac7f
18 changed files with 202 additions and 57 deletions

View File

@@ -232,7 +232,7 @@
"Tint": [120.0, 0.33, 2.0]
},
"Speed": 384,
"Friction": [4, 3],
"Friction": 4,
"Range": 35,
"Size": [10, 10],
"Special": "Poison",
@@ -455,7 +455,7 @@
"SpeedLow": -256,
"SpeedHigh": 256,
"SpeedScale": true,
"Friction": [4, 3],
"Friction": 4,
"RangeLow": 120,
"RangeHigh": 148,
"Power": 2,
@@ -484,7 +484,7 @@
},
"SpeedLow": 0,
"SpeedHigh": 255,
"Friction": [4, 3],
"Friction": 4,
"RangeLow": 155,
"RangeHigh": 191,
"Size": [10, 10],
@@ -509,7 +509,7 @@
},
"SpeedLow": 0,
"SpeedHigh": 255,
"Friction": [4, 3],
"Friction": 4,
"RangeLow": 155,
"RangeHigh": 191,
"Size": [10, 10],
@@ -552,6 +552,50 @@
"explosion2",
"explosion3"
]
},
{
"Name": "explosion_small",
"Pic": {
"Type": "Animated",
"Sprites": "explosion_small",
"TicksPerFrame": 4
},
"Speed": 0,
"Range": 27,
"Power": 3,
"Size": [32, 32],
"Persists": true,
"Spark": "",
"HitSounds": {
"Object": "hit_gas",
"Flesh": "hit_gas",
"Wall": ""
}
},
{
"Name": "rocket",
"Pic": {
"Type": "Directional",
"Sprites": "rocket_blue"
},
"ShadowSize": [2, 2],
"Speed": 250,
"Friction": -9,
"Range": 120,
"Power": 1,
"Size": [8, 8],
"Spark": "",
"HitSounds": {
"Object": "",
"Flesh": "",
"Wall": ""
},
"OutOfRangeGuns": [
"explosion_small"
],
"HitGuns": [
"explosion_small"
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,5 @@
Adapted from Explosion Set 1 (M484 Games)
By Master484
http://opengameart.org/content/explosion-set-1-m484-games
http://creativecommons.org/publicdomain/zero/1.0/

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -48,8 +48,7 @@
"Cost": 20,
"Lock": 30,
"Sound": "launch",
"ElevationLow": 24,
"ElevationHigh": 24
"Elevation": 24
},
{
"Index": 3,
@@ -92,8 +91,7 @@
"Cost": 20,
"Lock": 30,
"Sound": "launch",
"ElevationLow": 24,
"ElevationHigh": 24
"Elevation": 24
},
{
"Index": 7,
@@ -103,8 +101,7 @@
"Cost": 20,
"Lock": 30,
"Sound": "launch",
"ElevationLow": 24,
"ElevationHigh": 24
"Elevation": 24
},
{
"Index": 8,
@@ -151,8 +148,7 @@
"Cost": 7,
"Lock": 30,
"Sound": "launch",
"ElevationLow": 24,
"ElevationHigh": 24
"Elevation": 24
},
{
"Index": 12,
@@ -182,8 +178,7 @@
"Cost": 10,
"Lock": 30,
"Sound": "launch",
"ElevationLow": 24,
"ElevationHigh": 24
"Elevation": 24
},
{
"Index": 15,
@@ -224,6 +219,23 @@
"SpreadWidth": 0.4,
"Sound": "swarmer",
"MuzzleFlashParticle": "muzzle_flash_heatseeker"
},
{
"Name": "Launcher",
"Bullet": "rocket",
"Cost": 15,
"Lock": 70,
"Sound": "rocket_launch",
"Elevation": 25,
"MuzzleFlashParticle": "muzzle_flash_shotgun"
},
{
"Name": "Infinite Grenades",
"Bullet": "infinite_grenade",
"Cost": 0,
"Lock": 1000,
"Sound": "launch",
"Elevation": 24
}
],
"PseudoGuns" : [
@@ -267,8 +279,8 @@
"Sound": "",
"SpreadCount": 8,
"SpreadWidth": 0.79,
"ElevationLow": 17,
"ElevationHigh": 15
"ElevationLow": 15,
"ElevationHigh": 17
},
{
"Pic": "",
@@ -278,8 +290,7 @@
"SpreadCount": 8,
"SpreadWidth": 0.79,
"AngleOffset": 0.39,
"ElevationLow": 21,
"ElevationHigh": 21
"Elevation": 21
},
{
"Pic": "",
@@ -300,6 +311,12 @@
"Name": "triggeredmine",
"Bullet": "triggeredmine",
"Sound": "mine_trigger"
},
{
"Pic": "",
"Name": "explosion_small",
"Bullet": "explosion_small",
"Sound": "explosion_small"
}
]
}

View File

@@ -102,6 +102,12 @@
"Sprites": "smoke",
"Range": 29,
"TicksPerFrame": 6
},
{
"Name": "explosion_small",
"Sprites": "explosion_small",
"Range": 7,
"TicksPerFrame": 4
}
]
}

View File

@@ -0,0 +1,4 @@
Derived from cannon_boom7.wav by ReadeOnly
http://freesound.org/people/ReadeOnly/sounds/186951/
http://creativecommons.org/publicdomain/zero/1.0/

BIN
sounds/explosion_small.wav Normal file

Binary file not shown.

4
sounds/rocket_launch.txt Normal file
View File

@@ -0,0 +1,4 @@
4 projectile launches by Michel Baradari
http://opengameart.org/content/4-projectile-launches
http://creativecommons.org/licenses/by/3.0/

BIN
sounds/rocket_launch.wav Normal file

Binary file not shown.

View File

@@ -191,7 +191,7 @@ bool UpdateBullet(TMobileObject *obj, const int ticks)
}
Vec2i pos = Vec2iScale(Vec2iAdd(objPos, obj->vel), ticks);
const bool hitItem = HitItem(obj, pos);
const bool hitItem = HitItem(obj, pos, obj->bulletClass->Persists);
const Vec2i realPos = Vec2iFull2Real(pos);
// Falling (grenades)
@@ -239,24 +239,28 @@ bool UpdateBullet(TMobileObject *obj, const int ticks)
}
// Friction
const bool isDiagonal = obj->vel.x != 0 && obj->vel.y != 0;
int frictionComponent = isDiagonal ?
(int)round(obj->bulletClass->Friction / sqrt(2)) :
obj->bulletClass->Friction;
for (int i = 0; i < ticks; i++)
{
if (obj->vel.x > 0)
{
obj->vel.x -= obj->bulletClass->Friction.x;
obj->vel.x -= frictionComponent;
}
else if (obj->vel.x < 0)
{
obj->vel.x += obj->bulletClass->Friction.x;
obj->vel.x += frictionComponent;
}
if (obj->vel.y > 0)
{
obj->vel.y -= obj->bulletClass->Friction.y;
obj->vel.y -= frictionComponent;
}
else if (obj->vel.y < 0)
{
obj->vel.y += obj->bulletClass->Friction.y;
obj->vel.y += frictionComponent;
}
}
@@ -482,7 +486,7 @@ static void LoadBullet(
b->SpeedLow = MIN(b->SpeedLow, b->SpeedHigh);
b->SpeedHigh = MAX(b->SpeedLow, b->SpeedHigh);
LoadBool(&b->SpeedScale, node, "SpeedScale");
LoadVec2i(&b->Friction, node, "Friction");
LoadInt(&b->Friction, node, "Friction");
if (json_find_first_label(node, "Range"))
{
LoadInt(&b->RangeLow, node, "Range");
@@ -605,16 +609,7 @@ void BulletTerminate(BulletClasses *bullets)
void BulletAdd(const AddBullet add)
{
Vec2i pos = add.MuzzlePos;
if (!Vec2iEqual(add.BulletClass->Size, Vec2iZero()))
{
const int maxSize = MAX(
add.BulletClass->Size.x, add.BulletClass->Size.y);
double x, y;
GetVectorsForRadians(add.Angle, &x, &y);
pos = Vec2iAdd(pos, Vec2iReal2Full(
Vec2iNew((int)round(x * maxSize), (int)round(y * maxSize))));
}
const Vec2i pos = add.MuzzlePos;
TMobileObject *obj = CArrayGet(&gMobObjs, MobObjAdd(pos, add.PlayerIndex));
obj->vel = GetFullVectorsForRadians(add.Angle);
SetBulletProps(

View File

@@ -66,7 +66,7 @@ typedef struct
int SpeedLow;
int SpeedHigh;
bool SpeedScale; // whether to scale X/Y speed based on perspective
Vec2i Friction; // Amount to subtract from velocity per tick
int Friction; // Amount to subtract from velocity per tick
// -1 is infinite range
int RangeLow;
int RangeHigh;

View File

@@ -103,7 +103,8 @@ bool IsCollisionWallOrEdge(Map *map, Vec2i pos, Vec2i size)
return IsCollisionWithWall(pos, size);
}
int ItemsCollide(TTileItem *item1, TTileItem *item2, Vec2i pos)
static bool ItemsCollide(
const TTileItem *item1, const TTileItem *item2, const Vec2i pos)
{
int dx = abs(pos.x - item2->x);
int dy = abs(pos.y - item2->y);
@@ -178,6 +179,41 @@ TTileItem *GetItemOnTileInCollision(
return NULL;
}
void CollideAllItems(
const TTileItem *item, const Vec2i pos,
const int mask, const CollisionTeam team, const bool isDogfight,
CollideItemFunc func, void *data)
{
const Vec2i tv = Vec2iToTile(pos);
Vec2i dv;
// Check collisions with all other items on this tile, in all 8 directions
for (dv.y = -1; dv.y <= 1; dv.y++)
{
for (dv.x = -1; dv.x <= 1; dv.x++)
{
const Vec2i dtv = Vec2iAdd(tv, dv);
if (!MapIsTileIn(&gMap, dtv))
{
continue;
}
CArray *tileThings = &MapGetTile(&gMap, dtv)->things;
for (int i = 0; i < (int)tileThings->size; i++)
{
TTileItem *ti = ThingIdGetTileItem(CArrayGet(tileThings, i));
// Don't collide if items are on the same team
if (!CollisionIsOnSameTeam(ti, team, isDogfight))
{
if (item != ti &&
(ti->flags & mask) &&
ItemsCollide(item, ti, pos))
{
func(ti, data);
}
}
}
}
}
}
Vec2i GetWallBounceFullPos(
const Vec2i startFull, const Vec2i newFull, Vec2i *velFull)

View File

@@ -73,6 +73,11 @@ bool CollisionIsOnSameTeam(
const TTileItem *i, const CollisionTeam team, const bool isDogfight);
TTileItem *GetItemOnTileInCollision(
TTileItem *item, Vec2i pos, int mask, CollisionTeam team, int isDogfight);
typedef void (*CollideItemFunc)(TTileItem *, void *);
void CollideAllItems(
const TTileItem *item, const Vec2i pos,
const int mask, const CollisionTeam team, const bool isDogfight,
CollideItemFunc func, void *data);
// Resolve wall bounces
Vec2i GetWallBounceFullPos(

View File

@@ -510,12 +510,16 @@ void MobObjDestroy(int id)
m->isInUse = false;
}
int HitItem(TMobileObject *obj, Vec2i pos)
typedef struct
{
bool HasHit;
bool MultipleHits;
bool HasFirstCollision;
TMobileObject *Obj;
} HitItemData;
static void HitItemFunc(TTileItem *ti, void *data);
bool HitItem(TMobileObject *obj, const Vec2i pos, const bool multipleHits)
{
TTileItem *item;
int hasHit;
Vec2i realPos = Vec2iFull2Real(pos);
// Don't hit if no damage dealt
// This covers non-damaging debris explosions
if (obj->bulletClass->Power <= 0 &&
@@ -524,18 +528,36 @@ int HitItem(TMobileObject *obj, Vec2i pos)
return 0;
}
item = GetItemOnTileInCollision(
&obj->tileItem, realPos, TILEITEM_CAN_BE_SHOT, COLLISIONTEAM_NONE,
gCampaign.Entry.Mode == CAMPAIGN_MODE_DOGFIGHT);
hasHit = DamageSomething(
obj->vel, obj->bulletClass->Power, obj->flags, obj->player,
item,
obj->bulletClass->Special,
obj->soundLock <= 0 ? &obj->bulletClass->HitSound : NULL,
true);
if (hasHit && obj->soundLock <= 0)
{
obj->soundLock += SOUND_LOCK_MOBILE_OBJECT;
}
return hasHit;
// Get all items that collide
HitItemData data;
data.HasHit = false;
data.MultipleHits = multipleHits;
data.HasFirstCollision = false;
data.Obj = obj;
CollideAllItems(
&obj->tileItem, Vec2iFull2Real(pos),
TILEITEM_CAN_BE_SHOT, COLLISIONTEAM_NONE,
gCampaign.Entry.Mode == CAMPAIGN_MODE_DOGFIGHT,
HitItemFunc, &data);
return data.HasHit;
}
static void HitItemFunc(TTileItem *ti, void *data)
{
HitItemData *hData = data;
if (hData->HasFirstCollision && !hData->MultipleHits)
{
return;
}
hData->HasFirstCollision = true;
hData->HasHit = DamageSomething(
hData->Obj->vel, hData->Obj->bulletClass->Power,
hData->Obj->flags, hData->Obj->player,
ti,
hData->Obj->bulletClass->Special,
hData->Obj->soundLock <= 0 ? &hData->Obj->bulletClass->HitSound : NULL,
true);
if (hData->HasHit && hData->Obj->soundLock <= 0)
{
hData->Obj->soundLock += SOUND_LOCK_MOBILE_OBJECT;
}
}

View File

@@ -155,7 +155,7 @@ void MobObjsTerminate(void);
int MobObjAdd(Vec2i fullpos, int player);
void MobObjDestroy(int id);
void MobileObjectUpdate(TMobileObject *obj, int ticks);
int HitItem(TMobileObject *obj, Vec2i pos);
bool HitItem(TMobileObject *obj, const Vec2i pos, const bool multipleHits);
bool UpdateMobileObject(TMobileObject *obj, const int ticks);

View File

@@ -248,7 +248,7 @@ void SoundPlayAtPosition(
for (;;)
{
channel = Mix_PlayChannel(-1, data, 0);
if (channel >= 0)
if (channel >= 0 || device->channels > 128)
{
break;
}

View File

@@ -238,8 +238,15 @@ static void LoadGunDescription(
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");