コード例 #1
0
bool AIHasClearShot(const Vec2i from, const Vec2i to)
{
    // Perform 4 line tests - above, below, left and right
    // This is to account for possible positions for the muzzle
    Vec2i fromOffset = from;

    const int pad = 2;
    fromOffset.x = from.x - (ACTOR_W + pad) / 2;
    if (Vec2iToTile(fromOffset).x >= 0 &&
            !AIHasClearLine(fromOffset, to, IsPosNoSee))
    {
        return false;
    }
    fromOffset.x = from.x + (ACTOR_W + pad) / 2;
    if (Vec2iToTile(fromOffset).x < gMap.Size.x &&
            !AIHasClearLine(fromOffset, to, IsPosNoSee))
    {
        return false;
    }
    fromOffset.x = from.x;
    fromOffset.y = from.y - (ACTOR_H + pad) / 2;
    if (Vec2iToTile(fromOffset).y >= 0 &&
            !AIHasClearLine(fromOffset, to, IsPosNoSee))
    {
        return false;
    }
    fromOffset.y = from.y + (ACTOR_H + pad) / 2;
    if (Vec2iToTile(fromOffset).y < gMap.Size.y &&
            !AIHasClearLine(fromOffset, to, IsPosNoSee))
    {
        return false;
    }
    return true;
}
コード例 #2
0
int AIGoto(TActor *actor, Vec2i p, bool ignoreObjects)
{
    Vec2i a = Vec2iFull2Real(actor->Pos);
    Vec2i currentTile = Vec2iToTile(a);
    Vec2i goalTile = Vec2iToTile(p);
    AIGotoContext *c = &actor->aiContext->Goto;

    // If we are already there, bail
    // This can happen if AI is trying to track the player,
    // but the player has died, for example.
    if (Vec2iEqual(currentTile, goalTile))
    {
        return 0;
    }

    // If we are currently following an A* path,
    // and it is still valid, keep following it until
    // we have reached a new tile
    if (c && c->IsFollowing && AStarCloseToPath(c, currentTile, goalTile))
    {
        return AStarFollow(c, currentTile, &actor->tileItem, a);
    }
    else if (AIHasClearPath(a, p, ignoreObjects))
    {
        // Simple case: if there's a clear line between AI and target,
        // walk straight towards it
        return AIGotoDirect(a, p);
    }
    else
    {
        // We need to recalculate A*

        AStarContext ac;
        ac.Map = &gMap;
        ac.IsTileOk =
            ignoreObjects ? IsTileWalkable : IsTileWalkableAroundObjects;
        // First, if the goal tile is blocked itself,
        // find a nearby tile that can be walked to
        c->Goal = MapSearchTileAround(ac.Map, goalTile, ac.IsTileOk);

        c->PathIndex = 1;	// start navigating to the next path node
        ASPathDestroy(c->Path);
        c->Path = ASPathCreate(
                      &cPathNodeSource, &ac, &currentTile, &c->Goal);

        // In case we can't calculate A* for some reason,
        // try simple navigation again
        if (ASPathGetCount(c->Path) <= 1)
        {
            debug(
                D_MAX,
                "Error: can't calculate path from {%d, %d} to {%d, %d}",
                currentTile.x, currentTile.y,
                goalTile.x, goalTile.y);
            return AIGotoDirect(a, p);
        }

        return AStarFollow(c, currentTile, &actor->tileItem, a);
    }
}
コード例 #3
0
ファイル: collision.c プロジェクト: Wuzzy2/cdogs-sdl
void CollideAllItems(
	const TTileItem *item, const Vec2i pos,
	const int mask, const CollisionTeam team, const bool isPVP,
	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, isPVP))
				{
					if (item != ti &&
						(ti->flags & mask) &&
						ItemsCollide(item, ti, pos))
					{
						func(ti, data);
					}
				}
			}
		}
	}
}
コード例 #4
0
ファイル: collision.c プロジェクト: kodephys/cdogs-sdl
TTileItem *GetItemOnTileInCollision(
	TTileItem *item, Vec2i pos, int mask, CollisionTeam team, int isDogfight)
{
	Vec2i tv = Vec2iToTile(pos);
	Vec2i dv;
	if (!MapIsTileIn(&gMap, tv))
	{
		return NULL;
	}

	// 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++)
		{
			CArray *tileThings = &MapGetTile(&gMap, Vec2iAdd(tv, dv))->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))
					{
						return ti;
					}
				}
			}
		}
	}

	return NULL;
}
コード例 #5
0
// Use pathfinding to check that there is a path between
// source and destination tiles
bool AIHasPath(const Vec2i from, const Vec2i to, const bool ignoreObjects)
{
    // Quick first test: check there is a clear path
    if (AIHasClearPath(from, to, ignoreObjects))
    {
        return true;
    }
    // Pathfind
    AStarContext ac;
    ac.Map = &gMap;
    ac.IsTileOk = ignoreObjects ? IsTileWalkable : IsTileWalkableAroundObjects;
    Vec2i fromTile = Vec2iToTile(from);
    Vec2i toTile = MapSearchTileAround(ac.Map, Vec2iToTile(to), ac.IsTileOk);
    ASPath path = ASPathCreate(&cPathNodeSource, &ac, &fromTile, &toTile);
    size_t pathCount = ASPathGetCount(path);
    ASPathDestroy(path);
    return pathCount > 1;
}
コード例 #6
0
ファイル: health_pickup.c プロジェクト: Wuzzy2/cdogs-sdl
static bool TryPlacePickup(HealthPickups *h)
{
	Vec2i size = Vec2iNew(HEALTH_W, HEALTH_H);
	// Attempt to place one in unexplored area
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		if (!Vec2iIsZero(v) && !MapGetTile(h->map, Vec2iToTile(v))->isVisited)
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	// Attempt to place one in out-of-sight area
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		const Vec2i fullpos = Vec2iReal2Full(v);
		const TActor *closestPlayer = AIGetClosestPlayer(fullpos);
		if (!Vec2iIsZero(v) &&
			(!closestPlayer || CHEBYSHEV_DISTANCE(
			fullpos.x, fullpos.y,
			closestPlayer->Pos.x, closestPlayer->Pos.y) >= 256 * 150))
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	// Attempt to place one anyway
	for (int i = 0; i < 100; i++)
	{
		const Vec2i v = MapGenerateFreePosition(h->map, size);
		if (!Vec2iIsZero(v))
		{
			MapPlaceHealth(v);
			return true;
		}
	}
	return false;
}
コード例 #7
0
static bool IsPosNoSee(void *data, Vec2i pos)
{
    return MapGetTile(data, Vec2iToTile(pos))->flags & MAPTILE_NO_SEE;
}
コード例 #8
0
static bool IsPosNoWalkAroundObjects(void *data, Vec2i pos)
{
    return !IsTileWalkableAroundObjects(data, Vec2iToTile(pos));
}
コード例 #9
0
static bool IsPosNoWalk(void *data, Vec2i pos)
{
    return !IsTileWalkable(data, Vec2iToTile(pos));
}
コード例 #10
0
ファイル: sounds.c プロジェクト: NSYXin/cdogs-sdl
static bool IsPosNoSee(void *data, Vec2i pos)
{
	const Tile *t = MapGetTile(data, Vec2iToTile(pos));
	return t != NULL && (t->flags & MAPTILE_NO_SEE);
}
コード例 #11
0
ファイル: bullet_class.c プロジェクト: NSYXin/cdogs-sdl
bool UpdateBullet(TMobileObject *obj, const int ticks)
{
	obj->count += ticks;
	obj->soundLock = MAX(0, obj->soundLock - ticks);
	obj->specialLock = MAX(0, obj->specialLock - ticks);
	if (obj->count < obj->bulletClass->Delay)
	{
		return true;
	}

	if (obj->range >= 0 && obj->count > obj->range)
	{
		if (!gCampaign.IsClient)
		{
			FireGuns(obj, &obj->bulletClass->OutOfRangeGuns);
		}
		return false;
	}

	const Vec2i objPos = Vec2iNew(obj->x, obj->y);

	if (obj->bulletClass->SeekFactor > 0)
	{
		// Find the closest target to this bullet and steer towards it
		const TActor *owner = ActorGetByUID(obj->ActorUID);
		if (owner == NULL)
		{
			return false;
		}
		const TActor *target = AIGetClosestEnemy(objPos, owner, obj->flags);
		if (target && !target->dead)
		{
			for (int i = 0; i < ticks; i++)
			{
				obj->vel = SeekTowards(
					objPos, obj->vel,
					obj->bulletClass->SpeedLow, target->Pos,
					obj->bulletClass->SeekFactor);
			}
		}
	}

	Vec2i pos = Vec2iScale(Vec2iAdd(objPos, obj->vel), ticks);
	HitType hitItem = HIT_NONE;
	if (!gCampaign.IsClient)
	{
		hitItem = HitItem(obj, pos, obj->bulletClass->Persists);
	}
	const Vec2i realPos = Vec2iFull2Real(pos);

	// Falling (grenades)
	if (obj->bulletClass->Falling.GravityFactor != 0)
	{
		bool hasDropped = obj->z <= 0;
		for (int i = 0; i < ticks; i++)
		{
			obj->z += obj->dz;
			if (obj->z <= 0)
			{
				obj->z = 0;
				if (obj->bulletClass->Falling.Bounces)
				{
					obj->dz = -obj->dz / 2;
				}
				else
				{
					obj->dz = 0;
				}
				if (!hasDropped)
				{
					if (!gCampaign.IsClient)
					{
						FireGuns(obj, &obj->bulletClass->Falling.DropGuns);
					}
				}
				hasDropped = true;
				if (obj->bulletClass->Falling.DestroyOnDrop)
				{
					return false;
				}
				SoundPlayAt(
					&gSoundDevice,
					StrSound(obj->bulletClass->HitSound.Wall), realPos);
			}
			else
			{
				obj->dz -= obj->bulletClass->Falling.GravityFactor;
			}
			if (!obj->bulletClass->Falling.FallsDown)
			{
				obj->dz = MAX(0, obj->dz);
			}
		}
	}
	
	// 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 -= frictionComponent;
		}
		else if (obj->vel.x < 0)
		{
			obj->vel.x += frictionComponent;
		}

		if (obj->vel.y > 0)
		{
			obj->vel.y -= frictionComponent;
		}
		else if (obj->vel.y < 0)
		{
			obj->vel.y += frictionComponent;
		}
	}

	bool hitWall = false;
	if (!gCampaign.IsClient && hitItem == HIT_NONE)
	{
		hitWall =
			MapIsRealPosIn(&gMap, realPos) && ShootWall(realPos.x, realPos.y);
	}
	if (hitWall || hitItem != HIT_NONE)
	{
		GameEvent b = GameEventNew(GAME_EVENT_BULLET_BOUNCE);
		b.u.BulletBounce.UID = obj->UID;
		if (hitWall && !Vec2iIsZero(obj->vel))
		{
			b.u.BulletBounce.HitType = (int)HIT_WALL;
		}
		else
		{
			b.u.BulletBounce.HitType = (int)hitItem;
		}
		bool alive = true;
		if ((hitWall && !obj->bulletClass->WallBounces) ||
			((hitItem != HIT_NONE) && obj->bulletClass->HitsObjects))
		{
			b.u.BulletBounce.Spark = true;
			CASSERT(!gCampaign.IsClient, "Cannot process bounces as client");
			FireGuns(obj, &obj->bulletClass->HitGuns);
			if (hitWall || !obj->bulletClass->Persists)
			{
				alive = false;
			}
		}
		b.u.BulletBounce.BouncePos = Vec2i2Net(pos);
		b.u.BulletBounce.BounceVel = Vec2i2Net(obj->vel);
		if (hitWall && !Vec2iIsZero(obj->vel))
		{
			// Bouncing
			Vec2i bounceVel = obj->vel;
			pos = GetWallBounceFullPos(objPos, pos, &bounceVel);
			b.u.BulletBounce.BouncePos = Vec2i2Net(pos);
			b.u.BulletBounce.BounceVel = Vec2i2Net(bounceVel);
			obj->vel = bounceVel;
		}
		GameEventsEnqueue(&gGameEvents, b);
		if (!alive)
		{
			return false;
		}
	}
	if (!MapTryMoveTileItem(&gMap, &obj->tileItem, realPos))
	{
		obj->count = obj->range;
		return false;
	}
	obj->x = pos.x;
	obj->y = pos.y;

	if (obj->bulletClass->Erratic)
	{
		for (int i = 0; i < ticks; i++)
		{
			obj->vel.x += ((rand() % 3) - 1) * 128;
			obj->vel.y += ((rand() % 3) - 1) * 128;
		}
	}

	// Proximity function, destroy
	// Only check proximity every now and then
	if (obj->bulletClass->ProximityGuns.size > 0 && !(obj->count & 3))
	{
		if (!gCampaign.IsClient)
		{
			// Detonate the mine if there are characters in the tiles around it
			const Vec2i tv =
				Vec2iToTile(Vec2iFull2Real(pos));
			Vec2i dv;
			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;
					}
					if (TileHasCharacter(MapGetTile(&gMap, dtv)))
					{
						FireGuns(obj, &obj->bulletClass->ProximityGuns);
						return false;
					}
				}
			}
		}
	}

	return true;
}