/** * Set a new target, and rotate towards him if needed. * * Stack: 1 - An encoded tile of the unit/tile to target. * * @param script The script engine to operate on. * @return The new target. */ uint16 Script_Unit_SetTarget(ScriptEngine *script) { Unit *u; uint16 target; tile32 tile; int8 orientation; u = g_scriptCurrentUnit; target = STACK_PEEK(1); if (target == 0 || !Tools_Index_IsValid(target)) { u->targetAttack = 0; return 0; } tile = Tools_Index_GetTile(target); orientation = Tile_GetDirection(u->o.position, tile); u->targetAttack = target; if (!g_table_unitInfo[u->o.type].o.flags.hasTurret) { u->targetMove = target; Unit_SetOrientation(u, orientation, false, 0); } Unit_SetOrientation(u, orientation, false, 1); return u->targetAttack; }
/** * Rotate the unit to aim at the enemy. * * Stack: *none*. * * @param script The script engine to operate on. * @return 0 if the enemy is no longer there or if we are looking at him, 1 otherwise. */ uint16 Script_Unit_Rotate(ScriptEngine *script) { const UnitInfo *ui; Unit *u; uint16 index; int8 current; tile32 tile; int8 orientation; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; ui = &g_table_unitInfo[u->o.type]; if (ui->movementType != MOVEMENT_WINGER && u->currentDestination.tile != 0) return 1; index = ui->o.flags.hasTurret ? 1 : 0; /* Check if we are already rotating */ if (u->orientation[index].speed != 0) return 1; current = u->orientation[index].current; if (!Tools_Index_IsValid(u->targetAttack)) return 0; /* Check where we should rotate to */ tile = Tools_Index_GetTile(u->targetAttack); orientation = Tile_GetDirection(u->o.position, tile); /* If we aren't already looking at it, rotate */ if (orientation == current) return 0; Unit_SetOrientation(u, orientation, false, index); return 1; }
/** * Unknown function 0C5A. * * Stack: *none* * * @param script The script engine to operate on. * @return unknown. */ uint16 Script_Structure_Unknown0C5A(ScriptEngine *script) { tile32 tile; Structure *s; Unit *u; uint16 position; VARIABLE_NOT_USED(script); s = g_scriptCurrentStructure; if (s->o.linkedID == 0xFF) return 0; u = Unit_Get_ByIndex(s->o.linkedID); if (g_table_unitInfo[u->o.type].movementType == MOVEMENT_WINGER && Unit_SetPosition(u, s->o.position)) { s->o.linkedID = u->o.linkedID; u->o.linkedID = 0xFF; if (s->o.linkedID == 0xFF) Structure_SetState(s, STRUCTURE_STATE_IDLE); Object_Script_Variable4_Clear(&s->o); if (s->o.houseID == g_playerHouseID) Sound_Output_Feedback(g_playerHouseID + 49); return 1; } position = Structure_FindFreePosition(s, u->o.type == UNIT_HARVESTER); if (position == 0) return 0; u->o.seenByHouses |= s->o.seenByHouses; tile = Tile_Center(Tile_UnpackTile(position)); if (!Unit_SetPosition(u, tile)) return 0; s->o.linkedID = u->o.linkedID; u->o.linkedID = 0xFF; Unit_SetOrientation(u, Tile_GetDirection(s->o.position, u->o.position) & 0xE0, true, 0); Unit_SetOrientation(u, u->orientation[0].current, true, 1); if (u->o.houseID == g_playerHouseID && u->o.type == UNIT_HARVESTER) { GUI_DisplayHint(STR_SEARCH_FOR_SPICE_FIELDS_TO_HARVEST, 0x6A); } if (s->o.linkedID == 0xFF) Structure_SetState(s, STRUCTURE_STATE_IDLE); Object_Script_Variable4_Clear(&s->o); if (s->o.houseID != g_playerHouseID) return 1; if (s->o.type == STRUCTURE_REPAIR) return 1; Sound_Output_Feedback(g_playerHouseID + ((u->o.type == UNIT_HARVESTER) ? 68 : 30)); return 1; }
/** * Unknown function 0788. * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 0. Always. */ uint16 Script_Team_Unknown0788(ScriptEngine *script) { Team *t; tile32 tile; PoolFindStruct find; VARIABLE_NOT_USED(script); t = g_scriptCurrentTeam; if (t->target == 0) return 0; tile = Tools_Index_GetTile(t->target); find.houseID = t->houseID; find.index = 0xFFFF; find.type = 0xFFFF; while (true) { Unit *u; uint16 distance; uint16 packed; int16 orientation; u = Unit_Find(&find); if (u == NULL) break; if (u->team - 1 != t->index) continue; if (t->target == 0) { Unit_SetAction(u, ACTION_GUARD); continue; } distance = g_table_unitInfo[u->o.type].fireDistance << 8; if (u->actionID == ACTION_ATTACK && u->targetAttack == t->target) { if (u->targetMove != 0) continue; if (Tile_GetDistance(u->o.position, tile) >= distance) continue; } if (u->actionID != ACTION_ATTACK) Unit_SetAction(u, ACTION_ATTACK); orientation = (Tile_GetDirection(tile, u->o.position) & 0xC0) + Tools_RandomLCG_Range(0, 127); if (orientation < 0) orientation += 256; packed = Tile_PackTile(Tile_MoveByDirection(tile, orientation, distance)); if (Object_GetByPackedTile(packed) == NULL) { Unit_SetDestination(u, Tools_Index_Encode(packed, IT_TILE)); } else { Unit_SetDestination(u, Tools_Index_Encode(Tile_PackTile(tile), IT_TILE)); } Unit_SetTarget(u, t->target); } return 0; }
/** * Rotate the turret to look at a tile. * * Stack: 1 - Tile to look at. * * @param script The script engine to operate on. * @return 0 if looking at target, otherwise 1. */ uint16 Script_Structure_RotateTurret(ScriptEngine *script) { Structure *s; tile32 lookAt; Tile *tile; uint16 baseSpriteID; uint16 encoded; int16 rotation; int16 rotationNeeded; int16 rotateDiff; encoded = STACK_PEEK(1); if (encoded == 0) return 0; s = g_scriptCurrentStructure; lookAt = Tools_Index_GetTile(encoded); tile = &g_map[Tile_PackTile(s->o.position)]; /* Find the base sprite of the structure */ if (s->o.type == STRUCTURE_ROCKET_TURRET) { baseSpriteID = g_iconMap[g_iconMap[ICM_ICONGROUP_BASE_ROCKET_TURRET] + 2]; } else { baseSpriteID = g_iconMap[g_iconMap[ICM_ICONGROUP_BASE_DEFENSE_TURRET] + 2]; } rotation = tile->groundSpriteID - baseSpriteID; if (rotation < 0 || rotation > 7) return 1; /* Find what rotation we should have to look at the target */ rotationNeeded = Orientation_Orientation256ToOrientation8(Tile_GetDirection(s->o.position, lookAt)); /* Do we need to rotate */ if (rotationNeeded == rotation) return 0; /* Find the fastest way to rotate to the correct rotation */ rotateDiff = rotationNeeded - rotation; if (rotateDiff < 0) rotateDiff += 8; if (rotateDiff < 4) { rotation++; } else { rotation--; } rotation &= 0x7; /* Set the new sprites */ tile->groundSpriteID = baseSpriteID + rotation; s->rotationSpriteDiff = rotation; Map_Update(Tile_PackTile(s->o.position), 0, false); return 1; }
/** * Move the Unit to the target, and keep repeating this function till we * arrived there. When closing in on the target it will slow down the Unit. * It is wise to only use this function on Carry-Alls. * * Stack: *none*. * * @param script The script engine to operate on. * @return 1 if arrived, 0 if still busy. */ uint16 Script_Unit_MoveToTarget(ScriptEngine *script) { Unit *u; uint16 delay; tile32 tile; uint16 distance; int8 orientation; int16 diff; u = g_scriptCurrentUnit; if (u->targetMove == 0) return 0; tile = Tools_Index_GetTile(u->targetMove); distance = Tile_GetDistance(u->o.position, tile); if ((int16)distance < 128) { Unit_SetSpeed(u, 0); u->o.position.s.x += clamp((int16)(tile.s.x - u->o.position.s.x), -16, 16); u->o.position.s.y += clamp((int16)(tile.s.y - u->o.position.s.y), -16, 16); Unit_UpdateMap(2, u); if ((int16)distance < 32) return 1; script->delay = 2; script->script--; return 0; } orientation = Tile_GetDirection(u->o.position, tile); Unit_SetOrientation(u, orientation, false, 0); diff = abs(orientation - u->orientation[0].current); if (diff > 128) diff = 256 - diff; Unit_SetSpeed(u, (Tools_AdjustToGameSpeed(min(distance / 8, 255), 25, 255, true) * (255 - diff) + 128) / 256); delay = max((int16)distance / 1024, 1); Unit_UpdateMap(2, u); if (delay != 0) { script->delay = delay; script->script--; } return 0; }
/** * Find the direction a tile is, seen from the structure. If the tile is * invalid it gives the direction the structure is currently looking at. * * Stack: 1 - Tile to get the direction to, or the current direction of the * structure in case the tile is invalid. * * @param script The script engine to operate on. * @return The direction (value between 0 and 7, shifted to the left with 5). */ uint16 Script_Structure_GetDirection(ScriptEngine *script) { Structure *s; tile32 tile; uint16 encoded; s = g_scriptCurrentStructure; encoded = STACK_PEEK(1); if (!Tools_Index_IsValid(encoded)) return s->rotationSpriteDiff << 5; tile = Tools_Index_GetTile(encoded); return Orientation_Orientation256ToOrientation8(Tile_GetDirection(s->o.position, tile)) << 5; }
/** * Get the direction to a tile or our current direction. * * Stack: 1 - An encoded tile to get the direction to. * * @param script The script engine to operate on. * @return The direction to the encoded tile if valid, otherwise our current orientation. */ uint16 Script_Unit_GetOrientation(ScriptEngine *script) { Unit *u; uint16 encoded; u = g_scriptCurrentUnit; encoded = STACK_PEEK(1); if (Tools_Index_IsValid(encoded)) { tile32 tile; tile = Tools_Index_GetTile(encoded); return Tile_GetDirection(u->o.position, tile); } return u->orientation[0].current; }
/** * Set the current destination of a Unit, bypassing any pathfinding. * It is wise to only use this function on Carry-Alls. * * Stack: 1 - An encoded tile, the destination. * * @param script The script engine to operate on. * @return The value 0. Always. */ uint16 Script_Unit_SetDestinationDirect(ScriptEngine *script) { Unit *u; uint16 encoded; encoded = STACK_PEEK(1); if (!Tools_Index_IsValid(encoded)) return 0; u = g_scriptCurrentUnit; if (u->currentDestination.tile == 0 || g_table_unitInfo[u->o.type].flags.isNormalUnit) { u->currentDestination = Tools_Index_GetTile(encoded); } Unit_SetOrientation(u, Tile_GetDirection(u->o.position, u->currentDestination), false, 0); return 0; }
/** * 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; }
/** * Gets the distance from the given object to the given encoded index. * @param o The object. * @param encoded The encoded index. * @return The distance. */ uint16 Object_GetDistanceToEncoded(Object* o, uint16 encoded) { Structure* s; tile32 position; s = Tools_Index_GetStructure(encoded); if (s != NULL) { uint16 packed; position = s->o.position; packed = Tile_PackTile(position); packed += g_table_structure_layoutEdgeTiles[g_table_structureInfo[o->type].layout][(Orientation_256To8(Tile_GetDirection(o->position, position)) + 4) & 7]; position = Tile_UnpackTile(packed); } else { position = Tools_Index_GetTile(encoded); } return Tile_GetDistance(o->position, position); }