void AddObjectOld( int x, int y, Vec2i size, const TOffsetPic * pic, PickupType type, int tileFlags) { TObject *o = CArrayGet(&gObjs, ObjAdd( Vec2iNew(x, y), size, NULL, type, tileFlags)); o->pic = pic; o->wreckedPic = NULL; o->structure = 0; o->flags = 0; MapTryMoveTileItem(&gMap, &o->tileItem, Vec2iFull2Real(Vec2iNew(x, y))); }
int ObjAdd( Vec2i pos, Vec2i size, const char *picName, PickupType type, int tileFlags) { // Find an empty slot in actor list TObject *o = NULL; int i; for (i = 0; i < (int)gObjs.size; i++) { TObject *obj = CArrayGet(&gObjs, i); if (!obj->isInUse) { o = obj; break; } } if (o == NULL) { TObject obj; memset(&obj, 0, sizeof obj); CArrayPushBack(&gObjs, &obj); i = (int)gObjs.size - 1; o = CArrayGet(&gObjs, i); } memset(o, 0, sizeof *o); o->pic = NULL; o->wreckedPic = NULL; o->picName = picName; o->Type = type; o->structure = 0; o->flags = 0; o->tileItem.x = o->tileItem.y = -1; o->tileItem.flags = tileFlags; o->tileItem.kind = KIND_OBJECT; o->tileItem.getPicFunc = GetObjectPic; o->tileItem.getActorPicsFunc = NULL; o->tileItem.w = size.x; o->tileItem.h = size.y; o->tileItem.id = i; MapTryMoveTileItem(&gMap, &o->tileItem, Vec2iFull2Real(pos)); o->isInUse = true; return i; }
int MobObjAdd(const Vec2i fullpos, const int player, const int uid) { // Find an empty slot in mobobj list TMobileObject *obj = NULL; int i; for (i = 0; i < (int)gMobObjs.size; i++) { TMobileObject *m = CArrayGet(&gMobObjs, i); if (!m->isInUse) { obj = m; break; } } if (obj == NULL) { TMobileObject m; memset(&m, 0, sizeof m); CArrayPushBack(&gMobObjs, &m); i = (int)gMobObjs.size - 1; obj = CArrayGet(&gMobObjs, i); } memset(obj, 0, sizeof *obj); obj->x = fullpos.x; obj->y = fullpos.y; obj->player = player; obj->uid = uid; obj->tileItem.kind = KIND_MOBILEOBJECT; obj->tileItem.id = i; obj->soundLock = 0; obj->isInUse = true; obj->tileItem.x = obj->tileItem.y = -1; obj->tileItem.getPicFunc = NULL; obj->tileItem.getActorPicsFunc = NULL; obj->tileItem.drawFunc = (TileItemDrawFunc)BogusDraw; obj->tileItem.drawData.MobObjId = i; obj->updateFunc = UpdateMobileObject; MapTryMoveTileItem(&gMap, &obj->tileItem, Vec2iFull2Real(fullpos)); return i; }
void ObjAdd(const NMapObjectAdd amo) { // Find an empty slot in object list TObject *o = NULL; int i; for (i = 0; i < (int)gObjs.size; i++) { TObject *obj = CArrayGet(&gObjs, i); if (!obj->isInUse) { o = obj; break; } } if (o == NULL) { TObject obj; memset(&obj, 0, sizeof obj); CArrayPushBack(&gObjs, &obj); i = (int)gObjs.size - 1; o = CArrayGet(&gObjs, i); } memset(o, 0, sizeof *o); o->uid = amo.UID; o->Class = StrMapObject(amo.MapObjectClass); o->Health = amo.Health; o->tileItem.x = o->tileItem.y = -1; o->tileItem.flags = amo.TileItemFlags; o->tileItem.kind = KIND_OBJECT; o->tileItem.getPicFunc = GetObjectPic; o->tileItem.getActorPicsFunc = NULL; o->tileItem.size = o->Class->Size; o->tileItem.id = i; MapTryMoveTileItem(&gMap, &o->tileItem, Net2Vec2i(amo.Pos)); o->isInUse = true; }
void BulletAdd(const NAddBullet add) { const Vec2i pos = Net2Vec2i(add.MuzzlePos); // Find an empty slot in mobobj list TMobileObject *obj = NULL; int i; for (i = 0; i < (int)gMobObjs.size; i++) { TMobileObject *m = CArrayGet(&gMobObjs, i); if (!m->isInUse) { obj = m; break; } } if (obj == NULL) { TMobileObject m; memset(&m, 0, sizeof m); CArrayPushBack(&gMobObjs, &m); i = (int)gMobObjs.size - 1; obj = CArrayGet(&gMobObjs, i); } memset(obj, 0, sizeof *obj); obj->UID = add.UID; obj->bulletClass = StrBulletClass(add.BulletClass); obj->x = pos.x; obj->y = pos.y; obj->z = add.MuzzleHeight; obj->dz = add.Elevation; obj->vel = Vec2iFull2Real(Vec2iScale( GetFullVectorsForRadians(add.Angle), RAND_INT(obj->bulletClass->SpeedLow, obj->bulletClass->SpeedHigh))); if (obj->bulletClass->SpeedScale) { obj->vel.y = obj->vel.y * TILE_WIDTH / TILE_HEIGHT; } obj->PlayerUID = add.PlayerUID; obj->ActorUID = add.ActorUID; obj->range = RAND_INT( obj->bulletClass->RangeLow, obj->bulletClass->RangeHigh); obj->flags = add.Flags; if (obj->bulletClass->HurtAlways) { obj->flags |= FLAGS_HURTALWAYS; } obj->tileItem.kind = KIND_MOBILEOBJECT; obj->tileItem.id = i; obj->isInUse = true; obj->tileItem.x = obj->tileItem.y = -1; obj->tileItem.getPicFunc = NULL; obj->tileItem.getActorPicsFunc = NULL; obj->tileItem.drawFunc = NULL; obj->tileItem.drawData.MobObjId = i; obj->tileItem.CPic = obj->bulletClass->CPic; obj->tileItem.CPicFunc = GetBulletDrawContext; obj->tileItem.size = obj->bulletClass->Size; obj->tileItem.ShadowSize = obj->bulletClass->ShadowSize; obj->updateFunc = UpdateBullet; MapTryMoveTileItem(&gMap, &obj->tileItem, Vec2iFull2Real(pos)); }
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; }