/** * Link two variable4 values to eachother, and clean up existing values if * needed. * @param encodedFrom From where the link goes. * @param encodedTo To where the link goes. */ void Object_Script_Variable4_Link(uint16 encodedFrom, uint16 encodedTo) { Object* objectFrom; Object* objectTo; if (!Tools_Index_IsValid(encodedFrom)) return; if (!Tools_Index_IsValid(encodedTo)) return; objectFrom = Tools_Index_GetObject(encodedFrom); objectTo = Tools_Index_GetObject(encodedTo); if (objectFrom == NULL) return; if (objectTo == NULL) return; if (objectFrom->script.variables[4] != objectTo->script.variables[4]) { Object_Script_Variable4_Clear(objectFrom); Object_Script_Variable4_Clear(objectTo); } if (objectFrom->script.variables[4] != 0) return; Object_Script_Variable4_Set(objectFrom, encodedTo); Object_Script_Variable4_Set(objectTo, encodedFrom); return; }
/** * Get information about the unit, like hitpoints, current target, etc. * * Stack: 1 - Which information you would like. * * @param script The script engine to operate on. * @return The information you requested. */ uint16 Script_Unit_GetInfo(ScriptEngine *script) { const UnitInfo *ui; Unit *u; u = g_scriptCurrentUnit; ui = &g_table_unitInfo[u->o.type]; switch (STACK_PEEK(1)) { case 0x00: return u->o.hitpoints * 256 / ui->o.hitpoints; case 0x01: return Tools_Index_IsValid(u->targetMove) ? u->targetMove : 0; case 0x02: return ui->fireDistance << 8; case 0x03: return u->o.index; case 0x04: return u->orientation[0].current; case 0x05: return u->targetAttack; case 0x06: if (u->originEncoded == 0 || u->o.type == UNIT_HARVESTER) Unit_FindClosestRefinery(u); return u->originEncoded; case 0x07: return u->o.type; case 0x08: return Tools_Index_Encode(u->o.index, IT_UNIT); case 0x09: return u->movingSpeed; case 0x0A: return abs(u->orientation[0].target - u->orientation[0].current); case 0x0B: return u->currentDestination.tile == 0 ? 0 : 1; case 0x0C: return u->fireDelay == 0 ? 1 : 0; case 0x0D: return ui->flags.explodeOnDeath; case 0x0E: return Unit_GetHouseID(u); case 0x0F: return u->o.flags.s.byScenario ? 1 : 0; case 0x10: return u->orientation[ui->o.flags.hasTurret ? 1 : 0].current; case 0x11: return abs(u->orientation[ui->o.flags.hasTurret ? 1 : 0].target - u->orientation[ui->o.flags.hasTurret ? 1 : 0].current); case 0x12: return (ui->movementType & 0x40) == 0 ? 0 : 1; case 0x13: return (u->o.seenByHouses & (1 << g_playerHouseID)) == 0 ? 0 : 1; default: return 0; } }
/** * 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; }
/** * Set the new destination of the unit. * * Stack: 1 - An encoded index where to move to. * * @param script The script engine to operate on. * @return The value 0. Always. */ uint16 Script_Unit_SetDestination(ScriptEngine *script) { Unit *u; uint16 encoded; u = g_scriptCurrentUnit; encoded = STACK_PEEK(1); if (encoded == 0 || !Tools_Index_IsValid(encoded)) { u->targetMove = 0; return 0; } if (u->o.type == UNIT_HARVESTER) { Structure *s; s = Tools_Index_GetStructure(encoded); if (s == NULL) { u->targetMove = encoded; u->route[0] = 0xFF; return 0; } if (s->o.script.variables[4] != 0) return 0; } Unit_SetDestination(u, encoded); return 0; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * Unknown function 11B9. * * Stack: 1 - Encoded tile. * * @param script The script engine to operate on. * @return unknown. */ uint16 Script_Structure_Unknown11B9(ScriptEngine *script) { uint16 encoded; Unit *u; encoded = STACK_PEEK(1); if (!Tools_Index_IsValid(encoded)) return 0; if (Tools_Index_GetType(encoded) != IT_UNIT) return 0; u = Tools_Index_GetUnit(encoded); if (u == NULL) return 0; Object_Script_Variable4_Clear(&u->o); u->targetMove = 0; return 0; }
/** * 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; }