/** * 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; }
/** * 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; }
/** * 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; }
/** * Perform a random action when we are sitting idle, like rotating around. * * Stack: *none*. * * @param script The script engine to operate on. * @return The value 0. Always. */ uint16 Script_Unit_IdleAction(ScriptEngine *script) { Unit *u; uint16 random; uint16 movementType; uint16 i; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; random = Tools_RandomLCG_Range(0, 10); movementType = g_table_unitInfo[u->o.type].movementType; if (movementType != MOVEMENT_FOOT && movementType != MOVEMENT_TRACKED && movementType != MOVEMENT_WHEELED) return 0; if (movementType == MOVEMENT_FOOT && random > 8) { u->spriteOffset = Tools_Random_256() & 0x3F; Unit_UpdateMap(2, u); } if (random > 2) return 0; /* Ensure the order of Tools_Random_256() calls. */ i = (Tools_Random_256() & 1) == 0 ? 1 : 0; Unit_SetOrientation(u, Tools_Random_256(), false, i); 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; }
/** * Set the orientation of a unit. * * Stack: 1 - New orientation for unit. * * @param script The script engine to operate on. * @return The current orientation of the unit (it will move to the requested over time). */ uint16 Script_Unit_SetOrientation(ScriptEngine *script) { Unit *u; u = g_scriptCurrentUnit; Unit_SetOrientation(u, (int8)STACK_PEEK(1), false, 0); return u->orientation[0].current; }
/** * 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; }
/** * 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; }
static void Scenario_Load_Unit(const char *key, char *settings) { uint8 houseType, unitType, actionType; int8 orientation; uint16 hitpoints; tile32 position; Unit *u; char *split; VARIABLE_NOT_USED(key); /* The value should have 6 values separated by a ',' */ split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* First value is the House type */ houseType = House_StringToType(settings); if (houseType == HOUSE_INVALID) return; /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Second value is the Unit type */ unitType = Unit_StringToType(settings); if (unitType == UNIT_INVALID) return; /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Third value is the Hitpoints in percent (in base 256) */ hitpoints = atoi(settings); /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Fourth value is the position on the map */ position = Tile_UnpackTile(atoi(settings)); /* Find the next value in the ',' separated list */ settings = split + 1; split = strchr(settings, ','); if (split == NULL) return; *split = '\0'; /* Fifth value is orientation */ orientation = (int8)((uint8)atoi(settings)); /* Sixth value is the current state of the unit */ settings = split + 1; actionType = Unit_ActionStringToType(settings); if (actionType == ACTION_INVALID) return; u = Unit_Allocate(UNIT_INDEX_INVALID, unitType, houseType); if (u == NULL) return; u->o.flags.s.byScenario = true; u->o.hitpoints = hitpoints * g_table_unitInfo[unitType].o.hitpoints / 256; u->o.position = position; u->orientation[0].current = orientation; u->actionID = actionType; u->nextActionID = ACTION_INVALID; /* In case the above function failed and we are passed campaign 2, don't add the unit */ if (!Map_IsValidPosition(Tile_PackTile(u->o.position)) && g_campaignID > 2) { Unit_Free(u); return; } /* XXX -- There is no way this is ever possible, as the beingBuilt flag is unset by Unit_Allocate() */ if (!u->o.flags.s.isNotOnMap) Unit_SetAction(u, u->actionID); u->o.seenByHouses = 0x00; Unit_HouseUnitCount_Add(u, u->o.houseID); Unit_SetOrientation(u, u->orientation[0].current, true, 0); Unit_SetOrientation(u, u->orientation[0].current, true, 1); Unit_SetSpeed(u, 0); }
/** * Delivery of transport, either to structure or to a tile. * * Stack: *none*. * * @param script The script engine to operate on. * @return One if delivered, zero otherwise.. */ uint16 Script_Unit_TransportDeliver(ScriptEngine *script) { Unit *u; Unit *u2; VARIABLE_NOT_USED(script); u = g_scriptCurrentUnit; if (u->o.linkedID == 0xFF) return 0; if (Tools_Index_GetType(u->targetMove) == IT_UNIT) return 0; if (Tools_Index_GetType(u->targetMove) == IT_STRUCTURE) { const StructureInfo *si; Structure *s; s = Tools_Index_GetStructure(u->targetMove); si = &g_table_structureInfo[s->o.type]; if (s->o.type == STRUCTURE_STARPORT) { uint16 ret = 0; if (s->state == STRUCTURE_STATE_BUSY) { s->o.linkedID = u->o.linkedID; u->o.linkedID = 0xFF; u->o.flags.s.inTransport = false; u->amount = 0; Unit_UpdateMap(2, u); Voice_PlayAtTile(24, u->o.position); Structure_SetState(s, STRUCTURE_STATE_READY); ret = 1; } Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; return ret; } if ((s->state == STRUCTURE_STATE_IDLE || (si->o.flags.busyStateIsIncoming && s->state == STRUCTURE_STATE_BUSY)) && s->o.linkedID == 0xFF) { Voice_PlayAtTile(24, u->o.position); Unit_EnterStructure(Unit_Get_ByIndex(u->o.linkedID), s); Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; u->o.linkedID = 0xFF; u->o.flags.s.inTransport = false; u->amount = 0; Unit_UpdateMap(2, u); return 1; } Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; return 0; } if (!Map_IsValidPosition(Tile_PackTile(Tile_Center(u->o.position)))) return 0; u2 = Unit_Get_ByIndex(u->o.linkedID); if (!Unit_SetPosition(u2, Tile_Center(u->o.position))) return 0; if (u2->o.houseID == g_playerHouseID) { Voice_PlayAtTile(24, u->o.position); } Unit_SetOrientation(u2, u->orientation[0].current, true, 0); Unit_SetOrientation(u2, u->orientation[0].current, true, 1); Unit_SetSpeed(u2, 0); u->o.linkedID = u2->o.linkedID; u2->o.linkedID = 0xFF; if (u->o.linkedID != 0xFF) return 1; u->o.flags.s.inTransport = false; Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; return 1; }