/** * Gets the best target for the current team. * * Stack: *none*. * * @param script The script engine to operate on. * @return The encoded index of the best target or 0 if none found. */ uint16 Script_Team_FindBestTarget(ScriptEngine *script) { Team *t; PoolFindStruct find; VARIABLE_NOT_USED(script); t = g_scriptCurrentTeam; find.houseID = t->houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Unit *u; uint16 target; u = Unit_Find(&find); if (u == NULL) break; if (u->team - 1 != t->index) continue; target = Unit_FindBestTargetEncoded(u, t->action == TEAM_ACTION_KAMIKAZE ? 4 : 0); if (target == 0) continue; if (t->target == target) return target; t->target = target; t->targetTile = Tile_GetTileInDirectionOf(Tile_PackTile(u->o.position), Tools_Index_GetPackedTile(target)); return target; } return 0; }
/** * Calculate the route to a tile. * * Stack: 1 - An encoded tile to calculate the route to. * * @param script The script engine to operate on. * @return 0 if we arrived on location, 1 otherwise. */ uint16 Script_Unit_CalculateRoute(ScriptEngine *script) { Unit *u; uint16 encoded; uint16 packedSrc; uint16 packedDst; u = g_scriptCurrentUnit; encoded = STACK_PEEK(1); if (u->currentDestination.tile != 0 || !Tools_Index_IsValid(encoded)) return 1; packedSrc = Tile_PackTile(u->o.position); packedDst = Tools_Index_GetPackedTile(encoded); if (packedDst == packedSrc) { u->route[0] = 0xFF; u->targetMove = 0; return 0; } if (u->route[0] == 0xFF) { Pathfinder_Data res; uint8 buffer[42]; res = Script_Unit_Pathfinder(packedSrc, packedDst, buffer, 40); memcpy(u->route, res.buffer, min(res.routeSize, 14)); if (u->route[0] == 0xFF) { u->targetMove = 0; if (u->o.type == UNIT_SANDWORM) { script->delay = 720; } } } else { uint16 distance; distance = Tile_GetDistancePacked(packedDst, packedSrc); if (distance < 14) u->route[distance] = 0xFF; } if (u->route[0] == 0xFF) return 1; if (u->orientation[0].current != (int8)(u->route[0] * 32)) { Unit_SetOrientation(u, (int8)(u->route[0] * 32), false, 0); return 1; } if (!Unit_StartMovement(u)) { u->route[0] = 0xFF; return 0; } memmove(&u->route[0], &u->route[1], 13); u->route[13] = 0xFF; return 1; }
/** * Gets the average distance between current team members, and set the * position of the team to the average position. * * Stack: *none*. * * @param script The script engine to operate on. * @return The average distance. */ uint16 Script_Team_GetAverageDistance(ScriptEngine *script) { uint16 averageX = 0; uint16 averageY = 0; uint16 count = 0; uint16 distance = 0; Team *t; PoolFindStruct find; VARIABLE_NOT_USED(script); t = g_scriptCurrentTeam; find.houseID = t->houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Unit *u; u = Unit_Find(&find); if (u == NULL) break; if (t->index != u->team - 1) continue; count++; averageX += (u->o.position.x >> 8) & 0x3f; averageY += (u->o.position.y >> 8) & 0x3f; } if (count == 0) return 0; averageX /= count; averageY /= count; t->position = Tile_MakeXY(averageX, averageY); find.houseID = t->houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Unit *u; u = Unit_Find(&find); if (u == NULL) break; if (t->index != u->team - 1) continue; distance += Tile_GetDistanceRoundedUp(u->o.position, t->position); } distance /= count; if (t->target == 0 || t->targetTile == 0) return distance; if (Tile_GetDistancePacked(Tile_PackXY(averageX, averageY), Tools_Index_GetPackedTile(t->target)) <= 10) t->targetTile = 2; return distance; }
/** * Makes the current unit fire a bullet (or eat its target). * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 1 if the current unit fired/eat, 0 otherwise. */ uint16 Script_Unit_Fire(ScriptEngine *script) { const UnitInfo *ui; Unit *u; uint16 target; UnitType typeID; uint16 distance; bool fireTwice; uint16 damage; u = g_scriptCurrentUnit; target = u->targetAttack; if (target == 0 || !Tools_Index_IsValid(target)) return 0; if (u->o.type != UNIT_SANDWORM && target == Tools_Index_Encode(Tile_PackTile(u->o.position), IT_TILE)) u->targetAttack = 0; if (u->targetAttack != target) { Unit_SetTarget(u, target); return 0; } ui = &g_table_unitInfo[u->o.type]; if (u->o.type != UNIT_SANDWORM && u->orientation[ui->o.flags.hasTurret ? 1 : 0].speed != 0) return 0; if (Tools_Index_GetType(target) == IT_TILE && Object_GetByPackedTile(Tools_Index_GetPackedTile(target)) != NULL) Unit_SetTarget(u, target); if (u->fireDelay != 0) return 0; distance = Object_GetDistanceToEncoded(&u->o, target); if ((int16)(ui->fireDistance << 8) < (int16)distance) return 0; if (u->o.type != UNIT_SANDWORM && (Tools_Index_GetType(target) != IT_UNIT || g_table_unitInfo[Tools_Index_GetUnit(target)->o.type].movementType != MOVEMENT_WINGER)) { int16 diff = 0; int8 orientation; orientation = Tile_GetDirection(u->o.position, Tools_Index_GetTile(target)); diff = abs(u->orientation[ui->o.flags.hasTurret ? 1 : 0].current - orientation); if (ui->movementType == MOVEMENT_WINGER) diff /= 8; if (diff >= 8) return 0; } damage = ui->damage; typeID = ui->bulletType; fireTwice = ui->flags.firesTwice && u->o.hitpoints > ui->o.hitpoints / 2; if ((u->o.type == UNIT_TROOPERS || u->o.type == UNIT_TROOPER) && (int16)distance > 512) typeID = UNIT_MISSILE_TROOPER; switch (typeID) { case UNIT_SANDWORM: { Unit *u2; Unit_UpdateMap(0, u); u2 = Tools_Index_GetUnit(target); if (u2 != NULL) { u2->o.script.variables[1] = 0xFFFF; Unit_RemovePlayer(u2); Unit_HouseUnitCount_Remove(u2); Unit_Remove(u2); } Map_MakeExplosion(ui->explosionType, u->o.position, 0, 0); Voice_PlayAtTile(63, u->o.position); Unit_UpdateMap(1, u); u->amount--; script->delay = 12; if ((int8)u->amount < 1) Unit_SetAction(u, ACTION_DIE); } break; case UNIT_MISSILE_TROOPER: damage -= damage / 4; /* FALL-THROUGH */ case UNIT_MISSILE_ROCKET: case UNIT_MISSILE_TURRET: case UNIT_MISSILE_DEVIATOR: case UNIT_BULLET: case UNIT_SONIC_BLAST: { Unit *bullet; bullet = Unit_CreateBullet(u->o.position, typeID, Unit_GetHouseID(u), damage, target); if (bullet == NULL) return 0; bullet->originEncoded = Tools_Index_Encode(u->o.index, IT_UNIT); Voice_PlayAtTile(ui->bulletSound, u->o.position); Unit_Deviation_Decrease(u, 20); } break; default: break; } u->fireDelay = Tools_AdjustToGameSpeed(ui->fireDelay * 2, 1, 0xFFFF, true); if (fireTwice) { u->o.flags.s.fireTwiceFlip = !u->o.flags.s.fireTwiceFlip; if (u->o.flags.s.fireTwiceFlip) u->fireDelay = Tools_AdjustToGameSpeed(5, 1, 10, true) & 0xFF; } else { u->o.flags.s.fireTwiceFlip = false; } u->fireDelay += Tools_Random_256() & 1; Unit_UpdateMap(2, u); return 1; }